From b06a4c744161615c2235b6d3ff7d3a4d6d934962 Mon Sep 17 00:00:00 2001
From: Matei Adriel <rafaeladriel11@gmail.com>
Date: Thu, 11 Jul 2019 20:51:14 +0300
Subject: [PATCH]  curves for wires & importing logic gates

---
 .prettierrc.js                                |   7 +
 .vscode/settings.json                         |   6 +
 README.txt                                    |   2 +
 deploy.ts                                     |  44 +-
 docs/assets/gist.png                          | Bin 0 -> 7262 bytes
 docs/assets/gist_url.png                      | Bin 0 -> 3103 bytes
 docs/controls.md                              |  93 +++
 docs/import.md                                |  13 +
 docs/main.md                                  |  10 +
 docs/url.md                                   |  13 +
 package-lock.json                             |  41 +-
 src/index.html                                | 264 ++++----
 src/ts/common/component/interfaces.ts         |  12 +-
 src/ts/common/component/material.ts           |  33 +-
 src/ts/common/componentImporter/evalImport.ts |  23 +
 src/ts/common/componentImporter/fetchJson.ts  |   6 +
 src/ts/common/componentImporter/getGist.ts    |  27 +
 .../componentImporter/importComponent.ts      |  62 ++
 .../componentManager/componentManager.ts      | 606 +++++++++++-------
 src/ts/common/componentManager/interfaces.ts  |   8 +-
 src/ts/common/wires/wireManager.ts            | 100 +--
 src/ts/main.ts                                | 335 ++++++----
 22 files changed, 1117 insertions(+), 588 deletions(-)
 create mode 100644 .prettierrc.js
 create mode 100644 .vscode/settings.json
 create mode 100644 README.txt
 create mode 100644 docs/assets/gist.png
 create mode 100644 docs/assets/gist_url.png
 create mode 100644 docs/controls.md
 create mode 100644 docs/import.md
 create mode 100644 docs/main.md
 create mode 100644 docs/url.md
 create mode 100644 src/ts/common/componentImporter/evalImport.ts
 create mode 100644 src/ts/common/componentImporter/fetchJson.ts
 create mode 100644 src/ts/common/componentImporter/getGist.ts
 create mode 100644 src/ts/common/componentImporter/importComponent.ts

diff --git a/.prettierrc.js b/.prettierrc.js
new file mode 100644
index 0000000..8347fc4
--- /dev/null
+++ b/.prettierrc.js
@@ -0,0 +1,7 @@
+module.exports = {
+	semi: false,
+	trailingComma: 'none',
+	singleQuote: true,
+	printWidth: 80,
+	tabWidth: 4
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..106395f
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,6 @@
+{
+	"eslint.enable": true,
+	"editor.formatOnSave": true,
+	"prettier.eslintIntegration": true,
+	"eslint.validate": ["typescript", "typescriptreact"]
+}
\ No newline at end of file
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..2214bae
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,2 @@
+Pentru a rula aplicatia, deschideti dist/index.html.
+Codul sursa se afla in folderul src.
\ No newline at end of file
diff --git a/deploy.ts b/deploy.ts
index 077a072..811d559 100644
--- a/deploy.ts
+++ b/deploy.ts
@@ -1,53 +1,47 @@
-const { publish } = require("gh-pages")
-const { exec } = require("child_process")
-const { random } = require("random-emoji")
+const { publish } = require('gh-pages')
+const { exec } = require('child_process')
+const { random } = require('random-emoji')
 
 // const { publish } = require("gh-pages")
 
 const args = process.argv.splice(2)
 const randomEmoji = () => random({ count: 1 })[0].character
 
-const mFlag = ((args.indexOf("--message") + 1) || (args.indexOf("-m") + 1)) - 1
-const message = `${randomEmoji()}  ${(mFlag >= 0) ? args[mFlag + 1] : "automated update"} ${randomEmoji()}`
+const mFlag = (args.indexOf('--message') + 1 || args.indexOf('-m') + 1) - 1
+const message = `${mFlag >= 0 ? args[mFlag + 1] : 'automated update'}`
 
-console.log("Deploying...");
+console.log('Deploying...')
 
 const run = (command: string): Promise<string> => {
     return new Promise((res, rej) => {
         console.log(`🏃  Running: '${command}'`)
         //@ts-ignore
         exec(command, (err, stdout, stderr) => {
-            if (err != null)
-                rej(err)
-            else if (typeof (stderr) != "string")
-                rej(new Error(stderr))
-            else
-                res(stdout)
+            if (err != null) rej(err)
+            else if (typeof stderr != 'string') rej(new Error(stderr))
+            else res(stdout)
         })
     })
 }
 
-
-(async () => {
+;(async () => {
     try {
-        if (!args.includes("--skipBuild") && !args.includes("-sb"))
-            await run("npm run build")
-        await run("git add .")
+        if (!args.includes('--skipBuild') && !args.includes('-sb'))
+            await run('npm run build')
+        await run('git add .')
         await run(`git commit -m " ${message}  "`)
-        await run("git push origin master")
+        await run('git push origin master')
         await new Promise((res, rej) => {
-            console.log("🏃  Updating github pages")
+            console.log('🏃  Updating github pages')
             //@ts-ignore
-            publish("dist", (err) => {
-                if (err)
-                    rej(err)
+            publish('dist', err => {
+                if (err) rej(err)
 
                 console.log(`😄  Succesfully published to github pages`)
                 res(true)
             })
         })
-    }
-    catch (err) {
+    } catch (err) {
         console.log(`😭  Something went wrong: ${err}`)
     }
-})()
\ No newline at end of file
+})()
diff --git a/docs/assets/gist.png b/docs/assets/gist.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a8c226b9e0f4c546e35af55e4fd6057ed63dd44
GIT binary patch
literal 7262
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
GGywqUP1NlG

literal 0
HcmV?d00001

diff --git a/docs/assets/gist_url.png b/docs/assets/gist_url.png
new file mode 100644
index 0000000000000000000000000000000000000000..f067e15923df551e3e69af98435c9c8d438c52d4
GIT binary patch
literal 3103
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

literal 0
HcmV?d00001

diff --git a/docs/controls.md b/docs/controls.md
new file mode 100644
index 0000000..b815ce9
--- /dev/null
+++ b/docs/controls.md
@@ -0,0 +1,93 @@
+# Controls
+
+## Moving around
+
+-   To move around, just click anywhere in the enviroment, drag in the oposite of the direction you want to moe in.
+-   Release when you finished the desired movement.
+
+## Zooming
+
+-   To zoom in, scroll upwards.
+-   To zoom out, scroll downwards.
+-   The zoom will be applied in the position pointed by the mouse.
+
+## Moving a component
+
+-   To move a component, left click on it.
+-   The gate will follow your mouse
+-   Release when the gate got in the desired position
+
+## Deleting a component
+
+-   To remove a component, right click on it.
+
+## Connection 2 pins
+
+-   To connect 2 pins, first click on one of them.
+-   Click on the other pin
+
+> Note: You cannot connect 2 pins of the same type.
+
+## Deleting a wire
+
+-   To delete a wire, click on it
+
+## Opening the command palette
+
+-   To open the command palette, press ctrl + shift + p
+
+## Creating a simulation
+
+-   To create a simulation, click the first button from the top of the sidebar, then type the desired name.
+
+## Saving a simulation
+
+-   To save a simulation, follow one of the following actions:
+    1. Press ctrl + s
+    2. Open the command palette and type save, then press enter
+    3. Click on the 'simulation' button, then click 'save'
+
+## Opening a simulation
+
+-   To open a simulation, click 'open simulation', then click the name of the simulation
+
+## Deleting a simulation
+
+-   To delete a simulation, click 'open simulation', and then click the 'delete' icon on the row of your desired simulation.
+
+## Rewind to the latest save (undo)
+
+-   To rewind to the latest save, follow one of the following actions:
+    1. Press ctrl + z
+    2. Click 'simulation' and then click 'undo'
+
+## Downloading a simulation
+
+-   To download a simulation, follow one of the following actions:
+    1. Click 'simulation' and then type 'download'
+    2. Open the command palette, type 'download' and then press enter
+
+> Note: You can also type 'download --save' or 'download -s' in the command palette to also save the simulation before downloading it
+
+## Deleting a simulation
+
+-   To delete a simulation, press 'simulation' and then press 'delete'
+-   Press 'yes'
+
+## Refreshing the enviroment
+
+-   To refresh the enviroment (reload all components), follow one of the following actions:
+    1. Click 'simulation' and then click 'refresh'
+    2. Press sfhit + delete
+
+> Note: this won't refresh the whole window. To refresh the whole window, use the ui built in your browser
+
+> Note 2: this can be useful if you just edited a custom logic gate and you want to see the changes without refreshing the whole window
+
+## Clearing a simulation
+
+> Note: cleaning = deleting all logic gates wich are not connected to anything
+
+-   To clear a simulation follow one of the following actions:
+    1. Click 'simulation'and then click 'clean'
+    2. Press shift + delete
diff --git a/docs/import.md b/docs/import.md
new file mode 100644
index 0000000..ccdd87b
--- /dev/null
+++ b/docs/import.md
@@ -0,0 +1,13 @@
+# importing a logic gate
+
+## Opening the import palette
+
+-   To open the import palette, follow one of the following actions:
+
+1. Press ctrl + g
+2. Press 'custom gates' and then press 'import new gate'
+
+## Importing a logic gate
+
+-   Open the import palette
+-   Type a valid command (see **[the url parser](./url.md)**)
diff --git a/docs/main.md b/docs/main.md
new file mode 100644
index 0000000..ddf3f74
--- /dev/null
+++ b/docs/main.md
@@ -0,0 +1,10 @@
+# Welcome
+
+Hello, and welcome to my logic gate simulator! I know it can look kinda scary at first, so that's why i wrote these docs!
+
+I recomand reading the first 3 chapters before you start, and then only dig deeper into the others when you feel you mastered the basics!
+
+# Table of contents
+
+1. [Basic controls](./controls.md)
+2. [Importing a custom logic gate](import.md)
diff --git a/docs/url.md b/docs/url.md
new file mode 100644
index 0000000..dac88a2
--- /dev/null
+++ b/docs/url.md
@@ -0,0 +1,13 @@
+# The url parser
+
+If the first word is 'gist', the parser will automatcally try to fetch the github gist with the id equl to the second word:
+
+**_Eg_**:
+
+```
+gist 8886faa6f99a7d2667ea8aa2f81ace04
+```
+
+![example of a gist id](./assets/gist_url.png)
+
+Else, the parser will just try to fetch directly from the full string
diff --git a/package-lock.json b/package-lock.json
index 450faa4..a83d554 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2891,7 +2891,8 @@
                 "ansi-regex": {
                     "version": "2.1.1",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 },
                 "aproba": {
                     "version": "1.2.0",
@@ -2912,12 +2913,14 @@
                 "balanced-match": {
                     "version": "1.0.0",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 },
                 "brace-expansion": {
                     "version": "1.1.11",
                     "bundled": true,
                     "dev": true,
+                    "optional": true,
                     "requires": {
                         "balanced-match": "^1.0.0",
                         "concat-map": "0.0.1"
@@ -2932,17 +2935,20 @@
                 "code-point-at": {
                     "version": "1.1.0",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 },
                 "concat-map": {
                     "version": "0.0.1",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 },
                 "console-control-strings": {
                     "version": "1.1.0",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 },
                 "core-util-is": {
                     "version": "1.0.2",
@@ -3059,7 +3065,8 @@
                 "inherits": {
                     "version": "2.0.3",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 },
                 "ini": {
                     "version": "1.3.5",
@@ -3071,6 +3078,7 @@
                     "version": "1.0.0",
                     "bundled": true,
                     "dev": true,
+                    "optional": true,
                     "requires": {
                         "number-is-nan": "^1.0.0"
                     }
@@ -3085,6 +3093,7 @@
                     "version": "3.0.4",
                     "bundled": true,
                     "dev": true,
+                    "optional": true,
                     "requires": {
                         "brace-expansion": "^1.1.7"
                     }
@@ -3092,12 +3101,14 @@
                 "minimist": {
                     "version": "0.0.8",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 },
                 "minipass": {
                     "version": "2.3.5",
                     "bundled": true,
                     "dev": true,
+                    "optional": true,
                     "requires": {
                         "safe-buffer": "^5.1.2",
                         "yallist": "^3.0.0"
@@ -3116,6 +3127,7 @@
                     "version": "0.5.1",
                     "bundled": true,
                     "dev": true,
+                    "optional": true,
                     "requires": {
                         "minimist": "0.0.8"
                     }
@@ -3196,7 +3208,8 @@
                 "number-is-nan": {
                     "version": "1.0.1",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 },
                 "object-assign": {
                     "version": "4.1.1",
@@ -3208,6 +3221,7 @@
                     "version": "1.4.0",
                     "bundled": true,
                     "dev": true,
+                    "optional": true,
                     "requires": {
                         "wrappy": "1"
                     }
@@ -3293,7 +3307,8 @@
                 "safe-buffer": {
                     "version": "5.1.2",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 },
                 "safer-buffer": {
                     "version": "2.1.2",
@@ -3329,6 +3344,7 @@
                     "version": "1.0.2",
                     "bundled": true,
                     "dev": true,
+                    "optional": true,
                     "requires": {
                         "code-point-at": "^1.0.0",
                         "is-fullwidth-code-point": "^1.0.0",
@@ -3348,6 +3364,7 @@
                     "version": "3.0.1",
                     "bundled": true,
                     "dev": true,
+                    "optional": true,
                     "requires": {
                         "ansi-regex": "^2.0.0"
                     }
@@ -3391,12 +3408,14 @@
                 "wrappy": {
                     "version": "1.0.2",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 },
                 "yallist": {
                     "version": "3.0.3",
                     "bundled": true,
-                    "dev": true
+                    "dev": true,
+                    "optional": true
                 }
             }
         },
diff --git a/src/index.html b/src/index.html
index 1e19d1d..e4abec8 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1,146 +1,154 @@
 <!DOCTYPE html>
 <html lang="en">
+    <head>
+        <title>Logic gate simulator</title>
 
-<head>
-	<title>Game</title>
+        <meta
+            name="viewport"
+            content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0"
+        />
 
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0">
+        <style>
+            html,
+            body {
+                padding: 0;
+                margin: 0;
+                height: 100%;
+                width: 100%;
+            }
 
-	<style>
-		html,
-		body {
-			padding: 0;
-			margin: 0;
-			height: 100%;
-			width: 100%;
-		}
+            body {
+                background: #222;
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                flex-direction: column;
+            }
 
-		body {
-			background: #222;
-			display: flex;
-			justify-content: center;
-			align-items: center;
-			flex-direction: column;
-		}
+            .sk-folding-cube {
+                margin: 20px auto;
+                width: 40px;
+                height: 40px;
+                position: relative;
+                -webkit-transform: rotateZ(45deg);
+                transform: rotateZ(45deg);
+            }
 
-		.sk-folding-cube {
-			margin: 20px auto;
-			width: 40px;
-			height: 40px;
-			position: relative;
-			-webkit-transform: rotateZ(45deg);
-			transform: rotateZ(45deg);
-		}
+            .sk-folding-cube .sk-cube {
+                float: left;
+                width: 50%;
+                height: 50%;
+                position: relative;
+                -webkit-transform: scale(1.1);
+                -ms-transform: scale(1.1);
+                transform: scale(1.1);
+            }
 
-		.sk-folding-cube .sk-cube {
-			float: left;
-			width: 50%;
-			height: 50%;
-			position: relative;
-			-webkit-transform: scale(1.1);
-			-ms-transform: scale(1.1);
-			transform: scale(1.1);
-		}
+            .sk-folding-cube .sk-cube:before {
+                content: '';
+                position: absolute;
+                top: 0;
+                left: 0;
+                width: 100%;
+                height: 100%;
+                background-color: #bbb;
+                -webkit-animation: sk-foldCubeAngle 2.4s infinite linear both;
+                animation: sk-foldCubeAngle 2.4s infinite linear both;
+                -webkit-transform-origin: 100% 100%;
+                -ms-transform-origin: 100% 100%;
+                transform-origin: 100% 100%;
+            }
 
-		.sk-folding-cube .sk-cube:before {
-			content: '';
-			position: absolute;
-			top: 0;
-			left: 0;
-			width: 100%;
-			height: 100%;
-			background-color: #bbb;
-			-webkit-animation: sk-foldCubeAngle 2.4s infinite linear both;
-			animation: sk-foldCubeAngle 2.4s infinite linear both;
-			-webkit-transform-origin: 100% 100%;
-			-ms-transform-origin: 100% 100%;
-			transform-origin: 100% 100%;
-		}
+            .sk-folding-cube .sk-cube2 {
+                -webkit-transform: scale(1.1) rotateZ(90deg);
+                transform: scale(1.1) rotateZ(90deg);
+            }
 
-		.sk-folding-cube .sk-cube2 {
-			-webkit-transform: scale(1.1) rotateZ(90deg);
-			transform: scale(1.1) rotateZ(90deg);
-		}
+            .sk-folding-cube .sk-cube3 {
+                -webkit-transform: scale(1.1) rotateZ(180deg);
+                transform: scale(1.1) rotateZ(180deg);
+            }
 
-		.sk-folding-cube .sk-cube3 {
-			-webkit-transform: scale(1.1) rotateZ(180deg);
-			transform: scale(1.1) rotateZ(180deg);
-		}
+            .sk-folding-cube .sk-cube4 {
+                -webkit-transform: scale(1.1) rotateZ(270deg);
+                transform: scale(1.1) rotateZ(270deg);
+            }
 
-		.sk-folding-cube .sk-cube4 {
-			-webkit-transform: scale(1.1) rotateZ(270deg);
-			transform: scale(1.1) rotateZ(270deg);
-		}
+            .sk-folding-cube .sk-cube2:before {
+                -webkit-animation-delay: 0.3s;
+                animation-delay: 0.3s;
+            }
 
-		.sk-folding-cube .sk-cube2:before {
-			-webkit-animation-delay: 0.3s;
-			animation-delay: 0.3s;
-		}
+            .sk-folding-cube .sk-cube3:before {
+                -webkit-animation-delay: 0.6s;
+                animation-delay: 0.6s;
+            }
 
-		.sk-folding-cube .sk-cube3:before {
-			-webkit-animation-delay: 0.6s;
-			animation-delay: 0.6s;
-		}
+            .sk-folding-cube .sk-cube4:before {
+                -webkit-animation-delay: 0.9s;
+                animation-delay: 0.9s;
+            }
 
-		.sk-folding-cube .sk-cube4:before {
-			-webkit-animation-delay: 0.9s;
-			animation-delay: 0.9s;
-		}
+            @-webkit-keyframes sk-foldCubeAngle {
+                0%,
+                10% {
+                    -webkit-transform: perspective(140px) rotateX(-180deg);
+                    transform: perspective(140px) rotateX(-180deg);
+                    opacity: 0;
+                }
+                25%,
+                75% {
+                    -webkit-transform: perspective(140px) rotateX(0deg);
+                    transform: perspective(140px) rotateX(0deg);
+                    opacity: 1;
+                }
+                90%,
+                100% {
+                    -webkit-transform: perspective(140px) rotateY(180deg);
+                    transform: perspective(140px) rotateY(180deg);
+                    opacity: 0;
+                }
+            }
 
-		@-webkit-keyframes sk-foldCubeAngle {
-			0%,
-			10% {
-				-webkit-transform: perspective(140px) rotateX(-180deg);
-				transform: perspective(140px) rotateX(-180deg);
-				opacity: 0;
-			}
-			25%,
-			75% {
-				-webkit-transform: perspective(140px) rotateX(0deg);
-				transform: perspective(140px) rotateX(0deg);
-				opacity: 1;
-			}
-			90%,
-			100% {
-				-webkit-transform: perspective(140px) rotateY(180deg);
-				transform: perspective(140px) rotateY(180deg);
-				opacity: 0;
-			}
-		}
+            @keyframes sk-foldCubeAngle {
+                0%,
+                10% {
+                    -webkit-transform: perspective(140px) rotateX(-180deg);
+                    transform: perspective(140px) rotateX(-180deg);
+                    opacity: 0;
+                }
+                25%,
+                75% {
+                    -webkit-transform: perspective(140px) rotateX(0deg);
+                    transform: perspective(140px) rotateX(0deg);
+                    opacity: 1;
+                }
+                90%,
+                100% {
+                    -webkit-transform: perspective(140px) rotateY(180deg);
+                    transform: perspective(140px) rotateY(180deg);
+                    opacity: 0;
+                }
+            }
+        </style>
 
-		@keyframes sk-foldCubeAngle {
-			0%,
-			10% {
-				-webkit-transform: perspective(140px) rotateX(-180deg);
-				transform: perspective(140px) rotateX(-180deg);
-				opacity: 0;
-			}
-			25%,
-			75% {
-				-webkit-transform: perspective(140px) rotateX(0deg);
-				transform: perspective(140px) rotateX(0deg);
-				opacity: 1;
-			}
-			90%,
-			100% {
-				-webkit-transform: perspective(140px) rotateY(180deg);
-				transform: perspective(140px) rotateY(180deg);
-				opacity: 0;
-			}
-		}
-	</style>
+        <link
+            rel="stylesheet"
+            href="https://fonts.googleapis.com/icon?family=Material+Icons"
+        />
+        <link rel="stylesheet" href="style.css" />
+    </head>
 
-    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
-	<link rel='stylesheet' href='style.css'>
-</head>
-
-<body ondragstart="return false;" ondrop="return false;" oncontextmenu="return false">
-	<div class="sk-folding-cube">
-		<div class="sk-cube1 sk-cube"></div>
-		<div class="sk-cube2 sk-cube"></div>
-		<div class="sk-cube4 sk-cube"></div>
-		<div class="sk-cube3 sk-cube"></div>
-	</div>
-</body>
-
-</html>
\ No newline at end of file
+    <body
+        ondragstart="return false;"
+        ondrop="return false;"
+        oncontextmenu="return false"
+    >
+        <div class="sk-folding-cube">
+            <div class="sk-cube1 sk-cube"></div>
+            <div class="sk-cube2 sk-cube"></div>
+            <div class="sk-cube4 sk-cube"></div>
+            <div class="sk-cube3 sk-cube"></div>
+        </div>
+    </body>
+</html>
diff --git a/src/ts/common/component/interfaces.ts b/src/ts/common/component/interfaces.ts
index fd8e63d..45a2fcf 100644
--- a/src/ts/common/component/interfaces.ts
+++ b/src/ts/common/component/interfaces.ts
@@ -1,8 +1,8 @@
-import { Pin } from "../pin";
+import { Pin } from '../pin'
 
 export interface ComponentState {
-    position: [number,number]
-    scale: [number,number]
+    position: [number, number]
+    scale: [number, number]
     template: string
     id: number
 }
@@ -11,8 +11,8 @@ export interface activationContext {
     inputs: Pin[]
     outputs: Pin[]
     succes: (mes: string) => any
-    error: (mes:string) => any
-    color: (color:string) => void
+    error: (mes: string) => any
+    color: (color: string) => void
 }
 
-export type materialMode = "standard_image" | "color"
\ No newline at end of file
+export type materialMode = 'standard_image' | 'color' | 'url'
diff --git a/src/ts/common/component/material.ts b/src/ts/common/component/material.ts
index 21f33f5..2de609f 100644
--- a/src/ts/common/component/material.ts
+++ b/src/ts/common/component/material.ts
@@ -1,6 +1,6 @@
-import { svg, Part } from "lit-html";
-import { BehaviorSubject } from "rxjs";
-import { materialMode } from "./interfaces";
+import { svg, Part } from 'lit-html'
+import { BehaviorSubject } from 'rxjs'
+import { materialMode } from './interfaces'
 
 declare function require<T>(path: string): T
 
@@ -10,25 +10,28 @@ export class Material {
     private static images: {
         [key: string]: string
     } = {
-            and: require("../../../assets/and_gate.jpg"),
-            or: require("../../../assets/or_gate.png"),
-            xor: require("../../../assets/xor_gate.png"),
-            nor: require("../../../assets/nor_gate.png")
-        }
+        and: require('../../../assets/and_gate.jpg'),
+        or: require('../../../assets/or_gate.png'),
+        xor: require('../../../assets/xor_gate.png'),
+        nor: require('../../../assets/nor_gate.png')
+    }
 
+    public color = new BehaviorSubject<string>('rgba(0,0,0,0)')
 
-    public color = new BehaviorSubject<string>("rgba(0,0,0,0)")
-
-    constructor(public mode: string, public name: materialMode | string) {
-        if (this.mode === "color")
-            this.color.next(name)
+    constructor(public mode: materialMode, public data: string) {
+        if (this.mode === 'color') this.color.next(data)
     }
 
     innerHTML(x: partFactory, y: partFactory, w: partFactory, h: partFactory) {
+        const src =
+            this.mode === 'standard_image'
+                ? Material.images[this.data]
+                : this.data
+
         return svg`<foreignobject x=${x} y=${y} width=${w} height=${h}>
             <div class="component-container">
-                <img src=${Material.images[this.name]} height="97%" width="97%" draggable=false class="component">
+                <img src=${src} height="97%" width="97%" draggable=false class="component">
            </div>
         </foreignobject>`
     }
-}
\ No newline at end of file
+}
diff --git a/src/ts/common/componentImporter/evalImport.ts b/src/ts/common/componentImporter/evalImport.ts
new file mode 100644
index 0000000..1d4ddf0
--- /dev/null
+++ b/src/ts/common/componentImporter/evalImport.ts
@@ -0,0 +1,23 @@
+import { fecthAsJson } from './fetchJson'
+import { getFirstFileFromGist, getGist } from './getGist'
+
+export const evalImport = async <T>(
+    command: string,
+    extension = 'json'
+): Promise<T> => {
+    const words = command.split(' ')
+
+    let final: T
+
+    if (words.length === 1) {
+        if (extension === 'json') {
+            final = await fecthAsJson<T>(command)
+        } else {
+            final = ((await (await fetch(command)).text()) as unknown) as T
+        }
+    } else if (words[0] === 'gist') {
+        final = getFirstFileFromGist(await getGist(words[1]), extension)
+    }
+
+    return final
+}
diff --git a/src/ts/common/componentImporter/fetchJson.ts b/src/ts/common/componentImporter/fetchJson.ts
new file mode 100644
index 0000000..e091245
--- /dev/null
+++ b/src/ts/common/componentImporter/fetchJson.ts
@@ -0,0 +1,6 @@
+export const fecthAsJson = async <T>(url: string) => {
+    const res = await fetch(url)
+    const json = await res.json()
+
+    return json as T
+}
diff --git a/src/ts/common/componentImporter/getGist.ts b/src/ts/common/componentImporter/getGist.ts
new file mode 100644
index 0000000..98bb3d3
--- /dev/null
+++ b/src/ts/common/componentImporter/getGist.ts
@@ -0,0 +1,27 @@
+import { fecthAsJson } from './fetchJson'
+
+export interface Gist {
+    files: Record<string, { content: string }>
+}
+
+export const getGist = async (id: string) => {
+    const url = `https://api.github.com/gists/${id}`
+    const json = await fecthAsJson<Gist>(url)
+
+    return json
+}
+
+export const getFirstFileFromGist = (gist: Gist, extension = 'json') => {
+    const content =
+        gist.files[
+            Object.keys(gist.files).find(
+                name => name.indexOf(`.${extension}`) !== -1
+            )
+        ].content
+
+    if (extension === 'json') {
+        return JSON.parse(content)
+    }
+
+    return content
+}
diff --git a/src/ts/common/componentImporter/importComponent.ts b/src/ts/common/componentImporter/importComponent.ts
new file mode 100644
index 0000000..8faf643
--- /dev/null
+++ b/src/ts/common/componentImporter/importComponent.ts
@@ -0,0 +1,62 @@
+import { ComponentTemplate } from '../componentManager/interfaces'
+import { ComponentManager } from '../componentManager'
+import { materialMode } from '../component/interfaces'
+import { fecthAsJson } from './fetchJson'
+import { getGist, getFirstFileFromGist } from './getGist'
+import { evalImport } from './evalImport'
+
+export interface Importable {
+    name: string
+    activation: string
+    onClick?: string
+    inputs: number
+    outputs: number
+    material: {
+        mode: materialMode
+        data: string
+    }
+}
+
+const defaults: Importable = {
+    activation: 'ctx.outputs[0].value = ctx.inputs[0].value',
+    inputs: 1,
+    outputs: 1,
+    material: {
+        mode: 'color',
+        data: 'red'
+    },
+    name: 'Imported component'
+}
+
+export async function parseActivation(activaton: string) {
+    const words = activaton.split(' ')
+
+    if (words[0] === 'url') {
+        return await evalImport<string>(words.slice(1).join(' '), 'js')
+    } else {
+        return activaton
+    }
+}
+
+export async function importComponent(
+    manager: ComponentManager,
+    command: string
+): Promise<ComponentTemplate> {
+    const final: Importable = await evalImport(command)
+
+    const template: ComponentTemplate = {
+        ...defaults,
+        ...final,
+        editable: false,
+        version: '1.0.0',
+        imported: true,
+        importCommand: command
+    }
+
+    template.activation = await parseActivation(template.activation)
+
+    manager.templateStore.store.set(template.name, template)
+    manager.succes(`Succesfully imported component ${template.name}`)
+
+    return template
+}
diff --git a/src/ts/common/componentManager/componentManager.ts b/src/ts/common/componentManager/componentManager.ts
index 9d9d63a..9979636 100644
--- a/src/ts/common/componentManager/componentManager.ts
+++ b/src/ts/common/componentManager/componentManager.ts
@@ -1,38 +1,38 @@
-import { Singleton } from "@eix/utils";
-import { Component } from "../component";
-import { Subject, BehaviorSubject, fromEvent } from "rxjs";
-import { svg, SVGTemplateResult, html } from "lit-html";
-import { subscribe } from "lit-rx";
-import { Screen } from "../screen.ts";
-import { ManagerState, ComponentTemplate } from "./interfaces";
-import { Store } from "../store";
-import { KeyboardInput } from "@eix/input"
-import { success, error } from "toastr"
-import { ComponentTemplateStore } from "./componentTemplateStore";
-import { alertOptions } from "./alertOptions";
-import { WireManager } from "../wires";
-import { runCounter } from "../component/runCounter";
-import { Settings } from "../store/settings";
-import { download } from "./download";
-import { modal } from "../modals";
-import { map } from "rxjs/operators";
-import { persistent } from "../store/persistent";
-import { MDCTextField } from '@material/textfield';
-
-const defaultName = "default"
+import { Singleton } from '@eix/utils'
+import { Component } from '../component'
+import { Subject, BehaviorSubject, fromEvent } from 'rxjs'
+import { svg, SVGTemplateResult, html } from 'lit-html'
+import { subscribe } from 'lit-rx'
+import { Screen } from '../screen.ts'
+import { ManagerState, ComponentTemplate } from './interfaces'
+import { Store } from '../store'
+import { KeyboardInput } from '@eix/input'
+import { success, error } from 'toastr'
+import { ComponentTemplateStore } from './componentTemplateStore'
+import { alertOptions } from './alertOptions'
+import { WireManager } from '../wires'
+import { runCounter } from '../component/runCounter'
+import { Settings } from '../store/settings'
+import { download } from './download'
+import { modal } from '../modals'
+import { map } from 'rxjs/operators'
+import { persistent } from '../store/persistent'
+import { MDCTextField } from '@material/textfield'
+import { importComponent } from '../componentImporter/importComponent'
 
+const defaultName = 'default'
 
 @Singleton
 export class ComponentManager {
     public components: Component[] = []
     public svgs = new Subject<SVGTemplateResult>()
-    public placeholder = new BehaviorSubject("Create simulation")
-    public barAlpha = new BehaviorSubject<string>("0");
+    public placeholder = new BehaviorSubject('Create simulation')
+    public barAlpha = new BehaviorSubject<string>('0')
     public wireManager = new WireManager()
     public onTop: Component
     public templateStore = new ComponentTemplateStore()
 
-    private temporaryCommnad = ""
+    private temporaryCommnad = ''
     private clicked = false
     private ignoreKeyDowns = false
 
@@ -42,66 +42,79 @@ export class ComponentManager {
         offset: number
         scale: [number, number]
     } = {
-            offset: 50,
-            scale: [100, 100]
-        }
+        offset: 50,
+        scale: [100, 100]
+    }
 
-    private commandHistoryStore = new Store<string>("commandHistory")
-    private store = new Store<ManagerState>("simulationStates")
+    private commandHistoryStore = new Store<string>('commandHistory')
+    private store = new Store<ManagerState>('simulationStates')
 
-    private saveEvent = new KeyboardInput("s")
-    private createEvent = new KeyboardInput("m")
-    private closeInputEvent = new KeyboardInput("enter")
-    private ctrlEvent = new KeyboardInput("ctrl")
-    private palleteEvent = new KeyboardInput("p")
-    private undoEvent = new KeyboardInput("z")
-    private shiftEvent = new KeyboardInput("shift")
-    private refreshEvent = new KeyboardInput("r")
-    private clearEvent = new KeyboardInput("delete")
-    private upEvent = new KeyboardInput("up")
-    private downEvent = new KeyboardInput("down")
+    private saveEvent = new KeyboardInput('s')
+    private createEvent = new KeyboardInput('m')
+    private closeInputEvent = new KeyboardInput('enter')
+    private ctrlEvent = new KeyboardInput('ctrl')
+    private palleteEvent = new KeyboardInput('p')
+    private undoEvent = new KeyboardInput('z')
+    private shiftEvent = new KeyboardInput('shift')
+    private refreshEvent = new KeyboardInput('r')
+    private gEvent = new KeyboardInput('g')
+    private clearEvent = new KeyboardInput('delete')
+    private upEvent = new KeyboardInput('up')
+    private downEvent = new KeyboardInput('down')
 
-    @persistent<ComponentManager, string>(defaultName, "main")
+    @persistent<ComponentManager, string>(defaultName, 'main')
     public name: string
     public alertOptions = alertOptions
 
     private commandHistory: string[] = []
     private commands: {
-        [key: string]: (ctx: ComponentManager, args: string[], flags: string[]) => any
+        [key: string]: (
+            ctx: ComponentManager,
+            args: string[],
+            flags: string[]
+        ) => any
     } = {
-            clear(ctx: ComponentManager) {
-                ctx.clear()
-            },
-            save(ctx: ComponentManager) {
-                ctx.save()
-            },
-            ls(ctx: ComponentManager) {
-                const data = ctx.store.ls()
-                const message = data.join("\n")
+        clear(ctx: ComponentManager) {
+            ctx.clear()
+        },
+        save(ctx: ComponentManager) {
+            ctx.save()
+        },
+        ls(ctx: ComponentManager) {
+            const data = ctx.store.ls()
+            const message = data.join('\n')
 
-                success(message, "", ctx.alertOptions)
-            },
-            help(ctx: ComponentManager) {
-                success(`Usage: &ltcommand> <br>
+            success(message, '', ctx.alertOptions)
+        },
+        help(ctx: ComponentManager) {
+            success(
+                `Usage: &ltcommand> <br>
             Where &ltcommand> is one of:
                 <ul>
-                ${Object.keys(ctx.commands).map(val => `
+                ${Object.keys(ctx.commands)
+                    .map(
+                        val => `
                     <li>${val}</li>
-                `).join("")}
+                `
+                    )
+                    .join('')}
                 </ul>
-            `, "", ctx.alertOptions)
-            },
-            refresh(ctx: ComponentManager) {
-                ctx.refresh()
-            },
-            rewind(ctx:ComponentManager){
-                localStorage.clear()
-                success("Succesfully cleared localStorage!","",ctx.alertOptions)
-            },
-            ctp: this.templateStore.commands.template,
-            settings: this.settings.commands,
-            download
-        }
+            `,
+                '',
+                ctx.alertOptions
+            )
+        },
+        refresh(ctx: ComponentManager) {
+            ctx.refresh()
+        },
+        rewind(ctx: ComponentManager) {
+            localStorage.clear()
+            success('Succesfully cleared localStorage!', '', ctx.alertOptions)
+        },
+        ctp: this.templateStore.commands.template,
+        settings: this.settings.commands,
+        download
+    }
     private inputMode: string
 
     public gates = this.templateStore.store.lsChanges
@@ -110,24 +123,24 @@ export class ComponentManager {
     public file: {
         [key: string]: () => void
     } = {
-            clear: () => this.clear(),
-            clean: () => this.smartClear(),
-            save: () => this.save(),
-            undo: () => this.refresh(),
-            download: () => download(this, [], []),
-            delete: () => this.delete(this.name),
-            refresh: () => this.silentRefresh(true)
-        }
+        clear: () => this.clear(),
+        clean: () => this.smartClear(),
+        save: () => this.save(),
+        undo: () => this.refresh(),
+        download: () => download(this, [], []),
+        delete: () => this.delete(this.name),
+        refresh: () => this.silentRefresh(true)
+    }
 
     public shortcuts: {
         [key: string]: string
     } = {
-            clear: "shift delete",
-            clean: "delete",
-            save: "ctrl s",
-            undo: "ctrl z",
-            refresh: "ctrl r"
-        }
+        clear: 'shift delete',
+        clean: 'delete',
+        save: 'ctrl s',
+        undo: 'ctrl z',
+        refresh: 'ctrl r'
+    }
 
     constructor() {
         runCounter.increase()
@@ -136,22 +149,22 @@ export class ComponentManager {
 
         this.refresh()
 
-        fromEvent(document.body, "keydown").subscribe((e: KeyboardEvent) => {
-            if (this.barAlpha.value == "1") {
-                const elem = document.getElementById("nameInput")
+        fromEvent(document.body, 'keydown').subscribe((e: KeyboardEvent) => {
+            if (this.barAlpha.value == '1') {
+                const elem = document.getElementById('nameInput')
                 elem.focus()
-            }
-            else if (!this.ignoreKeyDowns) {
+            } else if (!this.ignoreKeyDowns) {
                 e.preventDefault()
             }
         })
 
-        fromEvent(document.body, "keyup").subscribe((e: MouseEvent) => {
-            if (this.barAlpha.value === "1") {
-                if (this.closeInputEvent.value)
-                    this.create()
-                else if (this.inputMode === "command") {
-                    const elem = <HTMLInputElement>document.getElementById("nameInput")
+        fromEvent(document.body, 'keyup').subscribe((e: MouseEvent) => {
+            if (this.barAlpha.value === '1') {
+                if (this.closeInputEvent.value) this.create()
+                else if (this.inputMode === 'command') {
+                    const elem = <HTMLInputElement>(
+                        document.getElementById('nameInput')
+                    )
                     if (this.upEvent.value) {
                         document.body.focus()
                         e.preventDefault()
@@ -159,10 +172,12 @@ export class ComponentManager {
 
                         if (index) {
                             //save drafts
-                            if (index === -1)
-                                this.temporaryCommnad = elem.value
+                            if (index === -1) this.temporaryCommnad = elem.value
 
-                            const newIndex = (index === -1) ? this.commandHistory.length - 1 : index - 1
+                            const newIndex =
+                                index === -1
+                                    ? this.commandHistory.length - 1
+                                    : index - 1
                             elem.value = this.commandHistory[newIndex]
                         }
                     }
@@ -173,36 +188,36 @@ export class ComponentManager {
 
                         if (index > -1) {
                             const maxIndex = this.commandHistory.length - 1
-                            elem.value = (index === maxIndex) ? this.temporaryCommnad : this.commandHistory[index + 1]
+                            elem.value =
+                                index === maxIndex
+                                    ? this.temporaryCommnad
+                                    : this.commandHistory[index + 1]
                         }
                     }
                 }
-            }
-            else {
+            } else {
                 if (this.ctrlEvent.value) {
                     if (this.createEvent.value) {
                         this.prepareNewSimulation()
-                    }
-                    else if (this.shiftEvent.value && this.palleteEvent.value) {
+                    } else if (
+                        this.shiftEvent.value &&
+                        this.palleteEvent.value
+                    ) {
                         this.preInput()
-                        this.inputMode = "command"
-                        this.placeholder.next("Command palette")
-                    }
-                    else if (this.saveEvent.value) {
+                        this.inputMode = 'command'
+                        this.placeholder.next('Command palette')
+                    } else if (this.gEvent.value) {
+                        this.importGate()
+                    } else if (this.saveEvent.value) {
                         this.save()
-                    }
-                    else if (this.undoEvent.value) {
+                    } else if (this.undoEvent.value) {
                         this.refresh()
-                    }
-                    else if (this.refreshEvent.value) {
+                    } else if (this.refreshEvent.value) {
                         this.silentRefresh(true)
                     }
-                }
-                else if (this.clearEvent.value) {
-                    if (this.shiftEvent.value)
-                        this.clear()
-                    else
-                        this.smartClear()
+                } else if (this.clearEvent.value) {
+                    if (this.shiftEvent.value) this.clear()
+                    else this.smartClear()
                 }
             }
         })
@@ -212,22 +227,20 @@ export class ComponentManager {
             this.update()
             // this.save()
         })
-        if (this.saves.value.length === 0)
-            this.save()
-
+        if (this.saves.value.length === 0) this.save()
     }
 
     private initEmptyGate(name: string) {
         const obj: ComponentTemplate = {
             inputs: 1,
             name,
-            version: "1.0.0",
+            version: '1.0.0',
             outputs: 1,
-            activation: "",
+            activation: '',
             editable: true,
             material: {
-                mode: "color",
-                data: "blue"
+                mode: 'color',
+                data: 'blue'
             }
         }
 
@@ -238,44 +251,57 @@ export class ComponentManager {
 
     public newGate() {
         this.preInput()
-        this.inputMode = "gate"
-        this.placeholder.next("Gate name")
+        this.inputMode = 'gate'
+        this.placeholder.next('Gate name')
+    }
+
+    public importGate() {
+        this.preInput()
+        this.inputMode = 'importGate'
+        this.placeholder.next('Gate url')
     }
 
     public prepareNewSimulation() {
         this.preInput()
-        this.inputMode = "create"
-        this.placeholder.next("Create simulation")
+        this.inputMode = 'create'
+        this.placeholder.next('Create simulation')
     }
 
     private preInput() {
-        const elem = <HTMLInputElement>document.getElementById("nameInput")
-        elem.value = ""
-        this.barAlpha.next("1")
+        const elem = <HTMLInputElement>document.getElementById('nameInput')
+        elem.value = ''
+        this.barAlpha.next('1')
     }
 
     private async create() {
-        const elem = <HTMLInputElement>document.getElementById("nameInput")
-        this.barAlpha.next("0")
+        const elem = <HTMLInputElement>document.getElementById('nameInput')
+        this.barAlpha.next('0')
 
-        if (this.inputMode === "create") {
+        if (this.inputMode === 'create') {
             await this.createEmptySimulation(elem.value)
-            success(`Succesfully created simulation ${elem.value}`, "", this.alertOptions)
+            success(
+                `Succesfully created simulation ${elem.value}`,
+                '',
+                this.alertOptions
+            )
+        } else if (this.inputMode === 'command') this.eval(elem.value)
+        else if (this.inputMode === 'gate') this.initEmptyGate(elem.value)
+        else if (this.inputMode === 'importGate') {
+            importComponent(this, elem.value)
         }
+    }
 
-        else if (this.inputMode === "command")
-            this.eval(elem.value)
-
-        else if (this.inputMode === "gate")
-            this.initEmptyGate(elem.value)
+    public succes(message: string) {
+        success(message, '', this.alertOptions)
     }
 
     private async handleDuplicateModal(name: string) {
         const result = await modal({
-            title: "Warning",
-            content: html`There was already a simulation called ${name},
-            are you sure you want to override it?
-            All your work will be lost!`
+            title: 'Warning',
+            content: html`
+                There was already a simulation called ${name}, are you sure you
+                want to override it? All your work will be lost!
+            `
         })
 
         return result
@@ -286,46 +312,80 @@ export class ComponentManager {
         const gate = this.templateStore.store.get(name)
 
         modal({
-            no: "",
-            yes: "save",
+            no: '',
+            yes: 'save',
             title: `Edit ${name}`,
-            content: html`${html`
-                <br>
-                <div class="mdc-text-field mdc-text-field--textarea">
-                    <textarea id="codeArea" class="mdc-text-field__input js" rows="8" cols="40">${
-                gate.activation
-                }</textarea>
-                    <div class="mdc-notched-outline">
-                        <div class="mdc-notched-outline__leading"></div>
-                        <div class="mdc-notched-outline__notch">
-                        <label for="textarea" class="mdc-floating-label">Activation function</label>
+            content: html`
+                ${html`
+                    <br />
+                    <div class="mdc-text-field mdc-text-field--textarea">
+                        <textarea
+                            id="codeArea"
+                            class="mdc-text-field__input js"
+                            rows="8"
+                            cols="40"
+                        >
+${gate.activation}</textarea
+                        >
+                        <div class="mdc-notched-outline">
+                            <div class="mdc-notched-outline__leading"></div>
+                            <div class="mdc-notched-outline__notch">
+                                <label for="textarea" class="mdc-floating-label"
+                                    >Activation function</label
+                                >
+                            </div>
+                            <div class="mdc-notched-outline__trailing"></div>
                         </div>
-                        <div class="mdc-notched-outline__trailing"></div>
                     </div>
-                </div><br><br>
-                <div class="mdc-text-field" id="inputCount">
-                    <input type="number" id="my-text-field" class="mdc-text-field__input inputCount-i" value=${gate.inputs}>
-                    <label class="mdc-floating-label" for="my-text-field">Inputs</label>
-                    <div class="mdc-line-ripple"></div>
-                </div><br><br>
-                <div class="mdc-text-field" id="outputCount">
-                    <input type="number" id="my-text-field" class="mdc-text-field__input outputCount-i" value=${gate.outputs}>
-                    <label class="mdc-floating-label" for="my-text-field">Outputs</label>
-                    <div class="mdc-line-ripple"></div>
-                </div><br><br>
-                <div class="mdc-text-field" id="color">
-                    <input type="string" id="my-text-field" class="mdc-text-field__input color-i" value=${gate.material.data}>
-                    <label class="mdc-floating-label" for="my-text-field">Outputs</label>
-                    <div class="mdc-line-ripple"></div>
-                </div><br>
-            `}`
+                    <br /><br />
+                    <div class="mdc-text-field" id="inputCount">
+                        <input
+                            type="number"
+                            id="my-text-field"
+                            class="mdc-text-field__input inputCount-i"
+                            value=${gate.inputs}
+                        />
+                        <label class="mdc-floating-label" for="my-text-field"
+                            >Inputs</label
+                        >
+                        <div class="mdc-line-ripple"></div>
+                    </div>
+                    <br /><br />
+                    <div class="mdc-text-field" id="outputCount">
+                        <input
+                            type="number"
+                            id="my-text-field"
+                            class="mdc-text-field__input outputCount-i"
+                            value=${gate.outputs}
+                        />
+                        <label class="mdc-floating-label" for="my-text-field"
+                            >Outputs</label
+                        >
+                        <div class="mdc-line-ripple"></div>
+                    </div>
+                    <br /><br />
+                    <div class="mdc-text-field" id="color">
+                        <input
+                            type="string"
+                            id="my-text-field"
+                            class="mdc-text-field__input color-i"
+                            value=${gate.material.data}
+                        />
+                        <label class="mdc-floating-label" for="my-text-field"
+                            >Color</label
+                        >
+                        <div class="mdc-line-ripple"></div>
+                    </div>
+                    <br />
+                `}
+            `
         }).then(val => {
             this.ignoreKeyDowns = false
             const elems: (HTMLInputElement | HTMLTextAreaElement)[] = [
-                document.querySelector("#codeArea"),
-                document.querySelector(".inputCount-i"),
-                document.querySelector(".outputCount-i"),
-                document.querySelector(".color-i")
+                document.querySelector('#codeArea'),
+                document.querySelector('.inputCount-i'),
+                document.querySelector('.outputCount-i'),
+                document.querySelector('.color-i')
             ]
             const data = elems.map(val => val.value)
 
@@ -334,21 +394,25 @@ export class ComponentManager {
                 activation: data[0],
                 inputs: Number(data[1]),
                 outputs: Number(data[2]),
-                material:{
-                    mode: "color",
+                material: {
+                    mode: 'color',
                     data: data[3]
                 }
             })
         })
 
-        new MDCTextField(document.querySelector('.mdc-text-field'));
-        new MDCTextField(document.querySelector('#outputCount'));
-        new MDCTextField(document.querySelector('#inputCount'));
-        new MDCTextField(document.querySelector('#color'));
+        new MDCTextField(document.querySelector('.mdc-text-field'))
+        new MDCTextField(document.querySelector('#outputCount'))
+        new MDCTextField(document.querySelector('#inputCount'))
+        new MDCTextField(document.querySelector('#color'))
     }
 
     public add(template: string, position?: [number, number]) {
-        const pos = position ? position : [...Array(2)].fill(this.standard.offset * this.components.length) as [number, number]
+        const pos = position
+            ? position
+            : ([...Array(2)].fill(
+                  this.standard.offset * this.components.length
+              ) as [number, number])
 
         this.components.push(new Component(template, pos, this.standard.scale))
         this.update()
@@ -356,17 +420,20 @@ export class ComponentManager {
 
     public async delete(name: string) {
         const res = await modal({
-            title: "Are you sure?",
-            content: html`Deleting a simulations is ireversible, and all work will be lost!`
+            title: 'Are you sure?',
+            content: html`
+                Deleting a simulations is ireversible, and all work will be
+                lost!
+            `
         })
 
         if (res) {
             if (this.name === name) {
                 if (this.saves.value.length > 1) {
                     this.switchTo(this.saves.value.find(val => val !== name))
-                }
-                else {
-                    let newName = (name === defaultName) ? `${defaultName}(1)` : defaultName
+                } else {
+                    let newName =
+                        name === defaultName ? `${defaultName}(1)` : defaultName
                     await this.createEmptySimulation(newName)
                     this.switchTo(newName)
                 }
@@ -384,15 +451,18 @@ export class ComponentManager {
                 scale: [1, 1]
             })
 
-            if (name !== this.name)
-                this.save()
+            if (name !== this.name) this.save()
             this.name = name
             this.refresh()
         }
 
-        return new Promise(async (res) => {//get wheater theres already a simulation with that name
-            if (this.store.get(name) && await this.handleDuplicateModal(name) ||
-                !this.store.get(name)) {
+        return new Promise(async res => {
+            //get wheater theres already a simulation with that name
+            if (
+                (this.store.get(name) &&
+                    (await this.handleDuplicateModal(name))) ||
+                !this.store.get(name)
+            ) {
                 create()
                 res(true)
             }
@@ -402,38 +472,53 @@ export class ComponentManager {
     public switchTo(name: string) {
         const data = this.store.get(name)
         if (!data)
-            error(`An error occured when trying to load ${name}`, "", this.alertOptions)
+            error(
+                `An error occured when trying to load ${name}`,
+                '',
+                this.alertOptions
+            )
 
         this.name = name
         this.refresh()
     }
 
     eval(command: string) {
-        if (!this.commandHistory.includes(command)) // no duplicates
+        if (!this.commandHistory.includes(command))
+            // no duplicates
             this.commandHistory.push(command)
 
-        while (this.commandHistory.length > 10) // max of 10 elements
+        while (
+            this.commandHistory.length > 10 // max of 10 elements
+        )
             this.commandHistory.shift()
 
-        const words = command.split(" ")
+        const words = command.split(' ')
 
         if (words[0] in this.commands) {
             const remaining = words.slice(1)
-            const flags = remaining.filter(val => val[0] == "-")
-            const args = remaining.filter(val => val[0] != "-")
+            const flags = remaining.filter(val => val[0] == '-')
+            const args = remaining.filter(val => val[0] != '-')
             this.commands[words[0]](this, args, flags)
-        }
-        else
-            error(`Command ${words} doesn't exist. Run help to get a list of all commands.`,
-                "", this.alertOptions)
+        } else
+            error(
+                `Command ${words} doesn't exist. Run help to get a list of all commands.`,
+                '',
+                this.alertOptions
+            )
     }
 
     public smartClear() {
         this.components = this.components.filter(({ id }) =>
-            this.wireManager.wires.find(val => val.input.of.id == id || val.output.of.id == id)
+            this.wireManager.wires.find(
+                val => val.input.of.id == id || val.output.of.id == id
+            )
         )
         this.update()
-        success("Succesfully cleared all unconnected components", "", this.alertOptions)
+        success(
+            'Succesfully cleared all unconnected components',
+            '',
+            this.alertOptions
+        )
     }
 
     public clear() {
@@ -441,7 +526,7 @@ export class ComponentManager {
         this.wireManager.dispose()
         this.update()
 
-        success("Succesfully cleared all components", "", this.alertOptions)
+        success('Succesfully cleared all components', '', this.alertOptions)
     }
 
     public refresh() {
@@ -454,7 +539,11 @@ export class ComponentManager {
 
         this.update()
 
-        success("Succesfully refreshed to the latest save", "", this.alertOptions)
+        success(
+            'Succesfully refreshed to the latest save',
+            '',
+            this.alertOptions
+        )
     }
 
     update() {
@@ -488,12 +577,10 @@ export class ComponentManager {
             // if (false) { }
             if (toAddOnTop >= 0) {
                 this.top(this.components[toAddOnTop])
-            }
-
-            else if (outsideComponents && this.clicked) {
+            } else if (outsideComponents && this.clicked) {
                 const mousePosition = [e.clientX, e.clientY]
-                const delta = mousePosition.map((value, index) =>
-                    this.screen.mousePosition[index] - value
+                const delta = mousePosition.map(
+                    (value, index) => this.screen.mousePosition[index] - value
                 ) as [number, number]
                 this.screen.move(...delta)
             }
@@ -503,7 +590,11 @@ export class ComponentManager {
     public silentRefresh(verboose = false) {
         this.loadState(this.state)
         if (verboose)
-            success("Succesfully reloaded all components", "", this.alertOptions)
+            success(
+                'Succesfully reloaded all components',
+                '',
+                this.alertOptions
+            )
     }
 
     public top(component: Component) {
@@ -523,57 +614,67 @@ export class ComponentManager {
                 toRemoveDuplicatesFor = component
             }
 
-            const stroke = subscribe(component.clickedChanges.pipe(map(
-                val => val ? "yellow" : "black"
-            )))
+            const stroke = subscribe(
+                component.clickedChanges.pipe(
+                    map(val => (val ? 'yellow' : 'black'))
+                )
+            )
 
             return svg`
             <g>
                 ${component.pinsSvg(10, 20)}
-                ${component.pinsSvg(10, 20, "output")}
+                ${component.pinsSvg(10, 20, 'output')}
 
-                <g @mousedown=${ (e: MouseEvent) => component.handleClick(e)}
+                <g @mousedown=${(e: MouseEvent) => component.handleClick(e)}
                     @touchstart=${(e: MouseEvent) => component.handleClick(e)}
                     @mouseup=${mouseupHandler}
                     @touchend=${mouseupHandler}>
-                    <rect width=${ subscribe(component.width)}
-                    height=${ subscribe(component.height)}
-                    x=${ subscribe(component.x)}
-                    y=${ subscribe(component.y)}
+                    <rect width=${subscribe(component.width)}
+                    height=${subscribe(component.height)}
+                    x=${subscribe(component.x)}
+                    y=${subscribe(component.y)}
                     stroke=${stroke}
-                    fill=${(component.material.mode === "standard_image") ?
-                    "rgba(0,0,0,0)" :
-                    subscribe(component.material.color)}
+                    fill=${
+                        component.material.mode !== 'color'
+                            ? 'rgba(0,0,0,0)'
+                            : subscribe(component.material.color)
+                    }
                     rx=20
                     ry=20>
                 </rect>
-                ${(component.material.mode === "standard_image") ? component.material.innerHTML(
-                        subscribe(component.x),
-                        subscribe(component.y),
-                        subscribe(component.width),
-                        subscribe(component.height)) : ""}
+                ${
+                    component.material.mode !== 'color'
+                        ? component.material.innerHTML(
+                              subscribe(component.x),
+                              subscribe(component.y),
+                              subscribe(component.width),
+                              subscribe(component.height)
+                          )
+                        : ''
+                }
                 </g>
             </g>
-        `});
+        `
+        })
 
-        if (toRemoveDuplicatesFor)
-            this.removeDuplicates(toRemoveDuplicatesFor)
+        if (toRemoveDuplicatesFor) this.removeDuplicates(toRemoveDuplicatesFor)
 
         return svg`${this.wireManager.svg} ${result}`
     }
 
     private removeDuplicates(component: Component) {
         let instances = this.components
-            .map((value, index) => (value == component) ? index : null)
+            .map((value, index) => (value == component ? index : null))
             .filter(value => value)
         instances.pop()
 
-        this.components = this.components
-            .filter((_, index) => instances.indexOf(index) != -1)
+        this.components = this.components.filter(
+            (_, index) => instances.indexOf(index) != -1
+        )
     }
 
     get state(): ManagerState {
-        const components = Array.from((new Set(this.components)).values())
+        const components = Array.from(new Set(this.components).values())
         return {
             components: components.map(value => value.state),
             position: this.screen.position as [number, number],
@@ -591,17 +692,24 @@ export class ComponentManager {
     }
 
     private loadState(state: ManagerState) {
-        if (!state.wires) //old state
+        if (!state.wires)
+            //old state
             return
 
         this.wireManager.dispose()
         this.clicked = false
-        this.components = state.components.map(value => Component.fromState(value))
+        this.components = state.components.map(value =>
+            Component.fromState(value)
+        )
         this.onTop = null
 
         state.wires.forEach(val => {
-            this.wireManager.start = this.getComponentById(val.from.owner).outputPins[val.from.index]
-            this.wireManager.end = this.getComponentById(val.to.owner).inputPins[val.to.index]
+            this.wireManager.start = this.getComponentById(
+                val.from.owner
+            ).outputPins[val.from.index]
+            this.wireManager.end = this.getComponentById(
+                val.to.owner
+            ).inputPins[val.to.index]
             this.wireManager.tryResolving()
         })
 
@@ -614,10 +722,14 @@ export class ComponentManager {
 
     save() {
         for (let i = 0; i < this.commandHistory.length; i++) {
-            const element = this.commandHistory[i];
+            const element = this.commandHistory[i]
             this.commandHistoryStore.set(i.toString(), element)
         }
         this.store.set(this.name, this.state)
-        success(`Saved the simulation ${this.name} succesfully!`, "", this.alertOptions)
+        success(
+            `Saved the simulation ${this.name} succesfully!`,
+            '',
+            this.alertOptions
+        )
     }
-}
\ No newline at end of file
+}
diff --git a/src/ts/common/componentManager/interfaces.ts b/src/ts/common/componentManager/interfaces.ts
index 96fb1e4..af17101 100644
--- a/src/ts/common/componentManager/interfaces.ts
+++ b/src/ts/common/componentManager/interfaces.ts
@@ -1,5 +1,5 @@
-import { ComponentState, materialMode } from "../component/interfaces";
-import { WireState } from "../wires/interface";
+import { ComponentState, materialMode } from '../component/interfaces'
+import { WireState } from '../wires/interface'
 
 export interface ManagerState {
     components: ComponentState[]
@@ -20,4 +20,6 @@ export interface ComponentTemplate {
         data: string
     }
     editable?: boolean
-}
\ No newline at end of file
+    imported?: boolean
+    importCommand?: string
+}
diff --git a/src/ts/common/wires/wireManager.ts b/src/ts/common/wires/wireManager.ts
index dbb323e..2d37f96 100644
--- a/src/ts/common/wires/wireManager.ts
+++ b/src/ts/common/wires/wireManager.ts
@@ -1,10 +1,11 @@
-import { Singleton } from "@eix/utils";
-import { Pin } from "../pin";
-import { Wire } from "./wire";
-import { svg } from "lit-html";
-import { subscribe } from "lit-rx";
-import { Subject } from "rxjs";
-import { WireStateVal } from "./interface";
+import { Singleton } from '@eix/utils'
+import { Pin } from '../pin'
+import { Wire } from './wire'
+import { svg } from 'lit-html'
+import { subscribe } from 'lit-rx'
+import { Subject, combineLatest } from 'rxjs'
+import { WireStateVal } from './interface'
+import { merge, map } from 'rxjs/operators'
 
 @Singleton
 export class WireManager {
@@ -15,20 +16,19 @@ export class WireManager {
 
     public update = new Subject<boolean>()
 
-    constructor() { }
+    constructor() {}
 
     public add(data: Pin) {
-        if (data.allowWrite) //output
+        if (data.allowWrite)
+            //output
             this.start = data
-        else
-            this.end = data
+        else this.end = data
 
         this.tryResolving()
     }
 
     public dispose() {
-        for (let i of this.wires)
-            i.dispose()
+        for (let i of this.wires) i.dispose()
 
         this.wires = []
     }
@@ -45,8 +45,7 @@ export class WireManager {
     }
 
     private canBind(end: Pin) {
-        if (this.wires.find(val => val.output === end))
-            return false
+        if (this.wires.find(val => val.output === end)) return false
         return true
     }
 
@@ -57,35 +56,60 @@ export class WireManager {
     }
 
     get svg() {
-        return svg`${this.wires.map((val) => {
+        return svg`${this.wires.map(val => {
             const i = val.input.of
             const o = val.output.of
             const inputIndex = i.outputPins.indexOf(val.input)
-            const input = i.piny(false, inputIndex)
-            const output = o.piny(true, o.inputPins.indexOf(val.output))
+            const inputY = i.piny(false, inputIndex)
+            const outputY = o.piny(true, o.inputPins.indexOf(val.output))
+
+            const output = [o.pinx(true, 20), outputY]
+            const input = [i.pinx(false, 20), inputY]
+            const midX = combineLatest(output[0], input[0]).pipe(
+                map(values => {
+                    return (values[0] + values[1]) / 2
+                })
+            )
+
+            const mid1 = [midX, outputY]
+            const mid2 = [midX, inputY]
+
+            const d = combineLatest<number[]>(
+                ...output,
+                ...mid1,
+                ...mid2,
+                ...input
+            ).pipe(
+                map(
+                    points =>
+                        `M ${points.slice(0, 2).join(' ')} C ${points
+                            .slice(2)
+                            .join(' ')}`
+                )
+            )
+
             return svg`
-            <line x2=${subscribe(i.pinx(false, 20))}
-                x1=${subscribe(o.pinx(true, 20))}
-                y2=${subscribe(input)}
-                y1=${subscribe(output)}
+            <path d=${subscribe(d)}  
                 stroke=${subscribe(val.input.svgColor)}
-                stroke-width=10
-                @click=${() => this.remove(val)}
-            >
-            </line>
-        `})}`
+                stroke-width=10 
+                fill="rgba(0,0,0,0)"
+                @click=${() => this.remove(val)} />
+        `
+        })}`
     }
 
     get state() {
-        return this.wires.map((val): WireStateVal => ({
-            from: {
-                owner: val.input.of.id,
-                index: val.input.of.outputPins.indexOf(val.input)
-            },
-            to: {
-                owner: val.output.of.id,
-                index: val.output.of.inputPins.indexOf(val.output)
-            }
-        }))
+        return this.wires.map(
+            (val): WireStateVal => ({
+                from: {
+                    owner: val.input.of.id,
+                    index: val.input.of.outputPins.indexOf(val.input)
+                },
+                to: {
+                    owner: val.output.of.id,
+                    index: val.output.of.inputPins.indexOf(val.output)
+                }
+            })
+        )
     }
-}
\ No newline at end of file
+}
diff --git a/src/ts/main.ts b/src/ts/main.ts
index 86a8c27..7d1d4d0 100644
--- a/src/ts/main.ts
+++ b/src/ts/main.ts
@@ -1,11 +1,12 @@
-import { render, html } from "lit-html"
-import { subscribe } from "lit-rx"
-import { Screen } from "./common/screen.ts";
-import { ComponentManager } from "./common/componentManager";
-import { map } from "rxjs/operators";
-import { MDCMenu } from '@material/menu';
-import { error } from "toastr"
-import { modal } from "./common/modals";
+import { render, html } from 'lit-html'
+import { subscribe } from 'lit-rx'
+import { Screen } from './common/screen.ts'
+import { ComponentManager } from './common/componentManager'
+import { map } from 'rxjs/operators'
+import { MDCMenu } from '@material/menu'
+import { error } from 'toastr'
+import { modal } from './common/modals'
+import { importComponent } from './common/componentImporter/importComponent'
 
 const screen = new Screen()
 
@@ -13,71 +14,75 @@ export const manager = new ComponentManager()
 manager.save()
 manager.update()
 
-window.onerror = (message: string, url: string, lineNumber: number): boolean => {
-    error(message, "", {
+window.onerror = (
+    message: string,
+    url: string,
+    lineNumber: number
+): boolean => {
+    error(message, '', {
         ...manager.alertOptions,
-        onclick: () => modal({
-            no: "",
-            yes: "close",
-            title: "Error",
-            content: html`
-                <table>
-                    <tr>
-                        <td>Url:</td>
-                        <td>${url}</td>
-                    </tr>
-                    <tr>
-                        <td>Message:</td>
-                        <td>${message}</td>
-                    </tr>
-                    <tr>
-                        <td>Line:</td>
-                        <td>${lineNumber}</td>
-                    </tr>
-                </table>
-            `
-        })
+        onclick: () =>
+            modal({
+                no: '',
+                yes: 'close',
+                title: 'Error',
+                content: html`
+                    <table>
+                        <tr>
+                            <td>Url:</td>
+                            <td>${url}</td>
+                        </tr>
+                        <tr>
+                            <td>Message:</td>
+                            <td>${message}</td>
+                        </tr>
+                        <tr>
+                            <td>Line:</td>
+                            <td>${lineNumber}</td>
+                        </tr>
+                    </table>
+                `
+            })
     })
 
-    return true;
-};
-
-const handleEvent = <T>(e: T, func: (e: T) => any) => {
-    if (manager.barAlpha.value == "0")
-        func(e)
-    else if (manager.barAlpha.value == "1"
-        && (e as unknown as MouseEvent).type == "mousedown"
-        && (e as unknown as MouseEvent).target != document.getElementById("nameInput"))
-        manager.barAlpha.next("0")
+    return true
 }
 
-const moveHandler = (e: MouseEvent) => handleEvent(e, (e: MouseEvent) => {
-    manager.handleMouseMove(e)
-    screen.updateMouse(e)
-})
+const handleEvent = <T>(e: T, func: (e: T) => any) => {
+    if (manager.barAlpha.value == '0') func(e)
+    else if (
+        manager.barAlpha.value == '1' &&
+        ((e as unknown) as MouseEvent).type == 'mousedown' &&
+        ((e as unknown) as MouseEvent).target !=
+            document.getElementById('nameInput')
+    )
+        manager.barAlpha.next('0')
+}
 
-render(html`
+const moveHandler = (e: MouseEvent) =>
+    handleEvent(e, (e: MouseEvent) => {
+        manager.handleMouseMove(e)
+        screen.updateMouse(e)
+    })
+
+render(
+    html`
     <div @mousemove=${moveHandler}
     @touchmove=${moveHandler}
-        @mousedown=${(e: MouseEvent) => handleEvent(e, () =>
-    manager.handleMouseDown()
-)}
-        @touchdown=${(e: MouseEvent) => handleEvent(e, () =>
-    manager.handleMouseDown()
-)}
-                @mouseup=${(e: MouseEvent) => handleEvent(e, () =>
-    manager.handleMouseUp()
-)}
-        @touchup=${(e: MouseEvent) => handleEvent(e, () =>
-    manager.handleMouseUp()
-)}
-                @wheel=${(e: MouseEvent) => handleEvent(e, (e: WheelEvent) =>
-    screen.handleScroll(e)
-)}>
+        @mousedown=${(e: MouseEvent) =>
+            handleEvent(e, () => manager.handleMouseDown())}
+        @touchdown=${(e: MouseEvent) =>
+            handleEvent(e, () => manager.handleMouseDown())}
+                @mouseup=${(e: MouseEvent) =>
+                    handleEvent(e, () => manager.handleMouseUp())}
+        @touchup=${(e: MouseEvent) =>
+            handleEvent(e, () => manager.handleMouseUp())}
+                @wheel=${(e: MouseEvent) =>
+                    handleEvent(e, (e: WheelEvent) => screen.handleScroll(e))}>
 
-        <div id=${subscribe(manager.barAlpha.pipe(map(val =>
-    (val == "1") ? "shown" : ""
-)))}
+        <div id=${subscribe(
+            manager.barAlpha.pipe(map(val => (val == '1' ? 'shown' : '')))
+        )}
         class=createBar>
             <div class="topContainer">
                 <div>
@@ -87,41 +92,42 @@ render(html`
                 </div>
             </div>
         </div>
-        <svg height=${ subscribe(screen.height)}
-            width=${ subscribe(screen.width)}
+        <svg height=${subscribe(screen.height)}
+            width=${subscribe(screen.width)}
             viewBox=${subscribe(screen.viewBox)}>
-            ${ subscribe(manager.svgs)}
+            ${subscribe(manager.svgs)}
         </svg>
     </div>
     <div class="ModalContainer"></div>
     <aside class="mdc-drawer main-sidebar">
     <div class="mdc-drawer__content">
         <nav class="mdc-list">
-        <a class="mdc-list-item mdc-list-item--activated" href="#" aria-current="page" @click=${() => manager.prepareNewSimulation()}>
+        <a class="mdc-list-item mdc-list-item--activated" href="#" aria-current="page" @click=${() =>
+            manager.prepareNewSimulation()}>
             <i class="material-icons mdc-list-item__graphic" aria-hidden="true">note_add</i>
             <span class="mdc-list-item__text">Create new simulation</span>
         </a>
         <a class="mdc-list-item" href="#" id="openSimulation" @click=${() => {
-        menus[0].open = true
-    }}>
+            menus[0].open = true
+        }}>
             <i class="material-icons mdc-list-item__graphic" aria-hidden="true">folder_open</i>
             <span class="mdc-list-item__text">Open simulation</span>
         </a>
         <a class="mdc-list-item" href="#" id="openFile" @click=${() => {
-        menus[2].open = true
-    }}>
+            menus[2].open = true
+        }}>
             <i class="material-icons mdc-list-item__graphic" aria-hidden="true">insert_drive_file</i>
             <span class="mdc-list-item__text">Simulation</span>
         </a>
         <a class="mdc-list-item" href="#" id="openCustomGates" @click=${() => {
-        menus[3].open = true
-    }}>
+            menus[3].open = true
+        }}>
             <i class="material-icons mdc-list-item__graphic" aria-hidden="true">edit</i>
             <span class="mdc-list-item__text">Custom gates</span>
         </a>
         <a class="mdc-list-item" href="#" id="openGates" @click=${() => {
-        menus[1].open = true
-    }}>
+            menus[1].open = true
+        }}>
             <i class="material-icons mdc-list-item__graphic" aria-hidden="true">add</i>
             <span class="mdc-list-item__text">Add component</span>
         </a>
@@ -132,61 +138,160 @@ render(html`
 
     <div class="mdc-menu mdc-menu-surface mdc-theme--primary-bg mdc-theme--on-primary" id="saveMenu">
         <ul class="mdc-list" role="menu" aria-hidden="true" aria-orientation="vertical" tabindex="-1">
-            ${subscribe(manager.saves.pipe(map(_ => _.map(val => html`
-                <li class= "mdc-list-item" role = "menuitem" @click=${() => manager.switchTo(val)}>
-                    <span class="mdc-list-item__text"> ${val} </span>
-                    <span class="material-icons mdc-list-item__meta" @click=${() => manager.delete(val)}> delete </span>
-                </li>`
-    ))))}
+            ${subscribe(
+                manager.saves.pipe(
+                    map(_ =>
+                        _.map(
+                            val => html`
+                                <li
+                                    class="mdc-list-item"
+                                    role="menuitem"
+                                    @click=${() => manager.switchTo(val)}
+                                >
+                                    <span class="mdc-list-item__text">
+                                        ${val}
+                                    </span>
+                                    <span
+                                        class="material-icons mdc-list-item__meta"
+                                        @click=${() => manager.delete(val)}
+                                    >
+                                        delete
+                                    </span>
+                                </li>
+                            `
+                        )
+                    )
+                )
+            )}
         </ul>
     </div>
 
     <div class="mdc-menu mdc-menu-surface mdc-theme--primary-bg mdc-theme--on-primary" id="gateMenu">
         <ul class="mdc-list" role="menu" aria-hidden="true" aria-orientation="vertical" tabindex="-1">
-            ${subscribe(manager.gates.pipe(map(_ => [..._].sort().map(val => html`
-                <li class= "mdc-list-item" role = "menuitem" @click=${() => manager.add(val)}>
-                    <span class="mdc-list-item__text"> ${val} </span>
-                    ${(manager.templateStore.store.get(val).editable ?
-            html`&nbsp &nbsp &nbsp <span class="material-icons mdc-list-item__meta" @click=${
-                () => manager.templateStore.store.delete(val)
-                }> delete </span>` :
-            ""
-        )}
-                </li>`
-    ))))}
+            ${subscribe(
+                manager.gates.pipe(
+                    map(gates =>
+                        [...gates].sort().map(name => {
+                            const gate = manager.templateStore.store.get(name)
+                            return html`
+                                <li
+                                    class="mdc-list-item"
+                                    role="menuitem"
+                                    @click=${() => manager.add(name)}
+                                >
+                                    <span class="mdc-list-item__text">
+                                        ${name}
+                                    </span>
+                                    ${gate.imported || gate.editable
+                                        ? html`
+                                              &nbsp &nbsp &nbsp
+                                              <span
+                                                  class="material-icons mdc-list-item__meta"
+                                                  @click=${(e: MouseEvent) => {
+                                                      e.preventDefault()
+                                                      e.stopPropagation()
+
+                                                      manager.templateStore.store.delete(
+                                                          name
+                                                      )
+                                                  }}
+                                              >
+                                                  delete
+                                              </span>
+                                          `
+                                        : ''}
+                                </li>
+                            `
+                        })
+                    )
+                )
+            )}
         </ul>
     </div>
 
     <div class="mdc-menu mdc-menu-surface mdc-theme--primary-bg mdc-theme--on-primary" id="fileMenu">
         <ul class="mdc-list" role="menu" aria-hidden="true" aria-orientation="vertical" tabindex="-1">
-            ${[...Object.keys(manager.file)].sort().map(key => html`
-                <li class= "mdc-list-item" role = "menuitem" @click=${() => manager.file[key]()}>
-                    <span class="mdc-list-item__text">${key}</span> 
-                    ${manager.shortcuts[key] ? html`
-                        <span class="mdc-list-item__meta">&nbsp &nbsp &nbsp ${manager.shortcuts[key]}</span>
-                    ` : ""}
-                </li>`
-    )}
+            ${[...Object.keys(manager.file)].sort().map(
+                key => html`
+                    <li
+                        class="mdc-list-item"
+                        role="menuitem"
+                        @click=${() => manager.file[key]()}
+                    >
+                        <span class="mdc-list-item__text">${key}</span>
+                        ${manager.shortcuts[key]
+                            ? html`
+                                  <span class="mdc-list-item__meta"
+                                      >&nbsp &nbsp &nbsp
+                                      ${manager.shortcuts[key]}</span
+                                  >
+                              `
+                            : ''}
+                    </li>
+                `
+            )}
         </ul>
     </div>
 
      <div class="mdc-menu mdc-menu-surface mdc-theme--primary-bg mdc-theme--on-primary" id="customGateMenu">
         <ul class="mdc-list" role="menu" aria-hidden="true" aria-orientation="vertical" tabindex="-1">
-           ${subscribe(manager.gates.pipe(map(_ => _
-        .filter(val => manager.templateStore.store.get(val).editable)
-        .map(val => html`
-                <li class= "mdc-list-item" role = "menuitem" @click=${() => manager.edit(val)}>
-                    <i class="material-icons mdc-list-item__graphic" aria-hidden="true">edit</i>
-                    <span class="mdc-list-item__text"> ${val} </span>
-                </li>`
-        ))))}
-            <li class= "mdc-list-item" role = "menuitem" @click=${() => manager.newGate()}>
+           ${subscribe(
+               manager.gates.pipe(
+                   map(gates =>
+                       gates
+                           .map(name => manager.templateStore.store.get(name))
+                           .filter(gate => gate.editable || gate.imported)
+                           .map(
+                               gate => html`
+                                   <li
+                                       class="mdc-list-item"
+                                       role="menuitem"
+                                       @click=${(e: MouseEvent) => {
+                                           e.preventDefault()
+                                           e.stopPropagation()
+
+                                           if (gate.editable) {
+                                               manager.edit(gate.name)
+                                           } else {
+                                               importComponent(
+                                                   manager,
+                                                   gate.importCommand
+                                               )
+                                           }
+                                       }}
+                                   >
+                                       <i
+                                           class="material-icons mdc-list-item__graphic"
+                                           aria-hidden="true"
+                                           >${gate.imported
+                                               ? 'refresh'
+                                               : 'edit'}</i
+                                       >
+                                       <span class="mdc-list-item__text">
+                                           ${gate.name}
+                                       </span>
+                                   </li>
+                               `
+                           )
+                   )
+               )
+           )}
+            <li class= "mdc-list-item" role = "menuitem" @click=${() =>
+                manager.newGate()}>
                 <i class="material-icons mdc-list-item__graphic" aria-hidden="true">add</i>
                 <span class="mdc-list-item__text"> New custom gate </span>
             </li>
+            <li class= "mdc-list-item" role = "menuitem" @click=${() =>
+                manager.importGate()}>
+                <i class="material-icons mdc-list-item__graphic" aria-hidden="true">add</i>
+                <span class="mdc-list-item__text"> Import new gate </span>
+                <span class="mdc-list-item__meta">&nbsp &nbsp &nbsp ctrl + g</span>
+            </li>
         </ul>
     </div>
-`, document.body)
+`,
+    document.body
+)
 
 const menus = [
     new MDCMenu(document.querySelector('#saveMenu')),
@@ -196,8 +301,8 @@ const menus = [
 ]
 menus.forEach(menu => menu.hoistMenuToBody())
 menus[0].setAnchorElement(document.querySelector(`#openSimulation`))
-menus[1].setAnchorElement(document.querySelector("#openGates"))
-menus[2].setAnchorElement(document.querySelector("#openFile"))
-menus[3].setAnchorElement(document.querySelector("#openCustomGates"))
+menus[1].setAnchorElement(document.querySelector('#openGates'))
+menus[2].setAnchorElement(document.querySelector('#openFile'))
+menus[3].setAnchorElement(document.querySelector('#openCustomGates'))
 
-manager.update()
\ No newline at end of file
+manager.update()