From bc16e1da8981380a8e0d0f7d53a3aa7d808e09b2 Mon Sep 17 00:00:00 2001
From: Quentin Bolsee <quentinbolsee@hotmail.com>
Date: Wed, 11 Oct 2023 10:10:11 -0400
Subject: [PATCH] deploy

---
 .gitignore        |   24 +
 .gitlab-ci.yml    |   20 +
 LICENSE.md        |    7 +
 index.html        |   81 +++
 package-lock.json | 1625 +++++++++++++++++++++++++++++++++++++++++++++
 package.json      |   25 +
 src/interface.js  |  464 +++++++++++++
 src/style.css     |  136 ++++
 src/vite-env.d.ts |    1 +
 tsconfig.json     |   23 +
 vite.config.js    |   21 +
 11 files changed, 2427 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 .gitlab-ci.yml
 create mode 100644 LICENSE.md
 create mode 100644 index.html
 create mode 100644 package-lock.json
 create mode 100644 package.json
 create mode 100644 src/interface.js
 create mode 100644 src/style.css
 create mode 100644 src/vite-env.d.ts
 create mode 100644 tsconfig.json
 create mode 100644 vite.config.js

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54f07af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..cd89b14
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,20 @@
+# This file is a template, and might need editing before it works on your project.
+# To contribute improvements to CI/CD templates, please follow the Development guide at:
+# https://docs.gitlab.com/ee/development/cicd/templates.html
+# This specific template is located at:
+# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml
+
+# Full project: https://gitlab.com/pages/plain-html
+pages:
+  stage: deploy
+  script:
+    - echo "The site will be deployed to $CI_PAGES_URL"
+    - mkdir .public
+    - cp -r ./dist .public
+    - rm -rf public
+    - mv .public public
+  artifacts:
+    paths:
+      - public
+  rules:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..51e2d4f
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,7 @@
+Copyright 2023, Developed by Jake Read and Quentin Bolsée at the Center for Bits and Atoms, MIT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..5553c88
--- /dev/null
+++ b/index.html
@@ -0,0 +1,81 @@
+<!-- Quentin Bolsee and Jake Read, MIT Center for Bits and Atoms, 2023 -->
+<!DOCTYPE html>
+<html>
+<head>
+	<link rel="icon" type="image/x-icon" href="images/logo.png">
+ 	<link rel="stylesheet" href="src/style.css">
+	<meta charset="utf-8">
+	<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
+	<meta http-equiv="Pragma" content="no-cache" />
+	<meta http-equiv="Expires" content="-1" />
+</head>
+<body style="background-color: rgb(255, 255, 255);">
+	<h1>gerber2png</h1>
+	<div id="dropZone"><p id="dropText">↑</p></div>
+	<input type="file" id="fileInput" multiple hidden></input>
+	<canvas id="canvas" hidden></canvas>
+	<div class="container">
+		<div class="panel">
+			<h2>Viewer</h2>
+			<div class="preview">
+				<svg id="previewSVG"></svg>
+				</div>
+			<div>
+			<!-- <button class="interface" id="loadButton">Load file(s)...</button></input> -->
+			<button class="interface" id="downloadRenderButton">Download render</button>
+			<button class="interface" id="downloadlayersButton">Download layers</button>
+			</div>
+		</div>
+		<div class="panel">
+			<h2>Settings</h2>
+			<div class="settings">
+				<h3>Rendering</h3>
+
+				<div>
+					<input type="checkbox" id="settingsFill" checked=checked>
+					<label for="settingsFill">Fill edge cut</label>
+				</div>
+
+				<div>
+					<input type="checkbox" id="settingsAsSVG" checked=unchecked>
+					<label for="settingsAsSVG">Download as SVG</label>
+				</div>
+
+				<h3>Resolution [DPI]</h3>
+
+				<div>
+					<input id="settingsDPI" step=1 min=1 type="number" value="1000">
+				</div>
+
+				<h3>Origin [mm]</h3>
+				<div>
+					<label for="settingsOrigX">x:</label>
+					<input id="settingsOrigX" step=0.1 type="number">
+					<label for="settingsOrigY">y:</label>
+					<input id="settingsOrigY" step=0.1 type="number">
+					<button class="lock" id="settingsLockOrig">🔓</button>
+				</div>
+
+				<h3>Dimensions [mm]</h3>
+				<div>
+					<label for="settingsDimX">x:</label>
+					<input id="settingsDimX" step=0.1 type="number">
+					<label for="settingsDimY">y:</label>
+					<input id="settingsDimY" step=0.1 type="number">
+					<button class="lock" id="settingsLockDim">🔓</button>
+				</div>
+
+				<h3>Margins [mm]</h3>
+				<div>
+					<label for="settingsMarginX">x:</label>
+					<input id="settingsMarginX" step=0.1 min=0 type="number" value=3>
+					<label for="settingsMarginY">y:</label>
+					<input id="settingsMarginY" step=0.1 min=0 type="number" value=3>
+				</div>
+			</div>
+		</div>
+	</div>
+	<script src="/src/interface.js" type="module"></script>
+	<script src="/src/interface.js" type="module"></script>
+</body>
+</html>
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..1a0b4ab
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1625 @@
+{
+  "name": "gerber2png",
+  "version": "0.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "gerber2png",
+      "version": "0.0.0",
+      "dependencies": {
+        "browserify-zlib": "^0.2.0",
+        "buffer": "^6.0.0",
+        "events": "^3.3.0",
+        "gerber-to-svg": "^4.2.8",
+        "process": "^0.11.10",
+        "save-svg-as-png": "^1.4.0",
+        "stream-browserify": "^3.0.0",
+        "util": "^0.12.5"
+      },
+      "devDependencies": {
+        "typescript": "^5.0.2",
+        "vite": "^4.4.11"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
+      "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
+      "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
+      "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
+      "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
+      "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
+      "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
+      "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
+      "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
+      "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
+      "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
+      "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
+      "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
+      "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
+      "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
+      "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
+      "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
+      "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
+      "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
+      "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
+      "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
+      "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+      "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@tracespace/xml-id": {
+      "version": "4.2.7",
+      "resolved": "https://registry.npmjs.org/@tracespace/xml-id/-/xml-id-4.2.7.tgz",
+      "integrity": "sha512-4T7uAx5HB6qwKNH7jQDiij4EDe+uP+zlFcccMpZageZA5S2V2GFxm3g5lThJdTaQhp8Or4FLsmu8C/BRdYS7hg=="
+    },
+    "node_modules/@types/node": {
+      "version": "20.8.4",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz",
+      "integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==",
+      "dev": true,
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "undici-types": "~5.25.1"
+      }
+    },
+    "node_modules/available-typed-arrays": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+      "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/browserify-zlib": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+      "dependencies": {
+        "pako": "~1.0.5"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "node_modules/call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
+      "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/android-arm": "0.18.20",
+        "@esbuild/android-arm64": "0.18.20",
+        "@esbuild/android-x64": "0.18.20",
+        "@esbuild/darwin-arm64": "0.18.20",
+        "@esbuild/darwin-x64": "0.18.20",
+        "@esbuild/freebsd-arm64": "0.18.20",
+        "@esbuild/freebsd-x64": "0.18.20",
+        "@esbuild/linux-arm": "0.18.20",
+        "@esbuild/linux-arm64": "0.18.20",
+        "@esbuild/linux-ia32": "0.18.20",
+        "@esbuild/linux-loong64": "0.18.20",
+        "@esbuild/linux-mips64el": "0.18.20",
+        "@esbuild/linux-ppc64": "0.18.20",
+        "@esbuild/linux-riscv64": "0.18.20",
+        "@esbuild/linux-s390x": "0.18.20",
+        "@esbuild/linux-x64": "0.18.20",
+        "@esbuild/netbsd-x64": "0.18.20",
+        "@esbuild/openbsd-x64": "0.18.20",
+        "@esbuild/sunos-x64": "0.18.20",
+        "@esbuild/win32-arm64": "0.18.20",
+        "@esbuild/win32-ia32": "0.18.20",
+        "@esbuild/win32-x64": "0.18.20"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
+    "node_modules/for-each": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+      "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+      "dependencies": {
+        "is-callable": "^1.1.3"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "node_modules/gerber-parser": {
+      "version": "4.2.7",
+      "resolved": "https://registry.npmjs.org/gerber-parser/-/gerber-parser-4.2.7.tgz",
+      "integrity": "sha512-Docb3egdLPjqYu/oH9Zhhc25kb2Ub9qkVFXu3seg13nDNW+KxbglqvyDcC0CGxkXxCaTqP3iyWNBG38fssmGKw==",
+      "dependencies": {
+        "@types/node": "^13.1.6",
+        "inherits": "^2.0.4",
+        "lodash.isfinite": "^3.3.2",
+        "lodash.padend": "^4.6.1",
+        "lodash.padstart": "^4.6.1",
+        "readable-stream": "^3.4.0"
+      }
+    },
+    "node_modules/gerber-parser/node_modules/@types/node": {
+      "version": "13.13.52",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
+      "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ=="
+    },
+    "node_modules/gerber-plotter": {
+      "version": "4.2.8",
+      "resolved": "https://registry.npmjs.org/gerber-plotter/-/gerber-plotter-4.2.8.tgz",
+      "integrity": "sha512-n+Kg4HJQzCVBvgh73Rit8xCrQOJFhnnIBV7psboURjlcIwuYum4eAEOTd6S3GyXXvxGGay3aOmp6HwQ373ciuQ==",
+      "dependencies": {
+        "@types/node": "^13.1.6",
+        "inherits": "^2.0.4",
+        "lodash.fill": "^3.4.0",
+        "lodash.isfinite": "^3.3.2",
+        "lodash.isfunction": "^3.0.9",
+        "readable-stream": "^3.4.0"
+      },
+      "peerDependencies": {
+        "gerber-parser": "^4.0.0"
+      }
+    },
+    "node_modules/gerber-plotter/node_modules/@types/node": {
+      "version": "13.13.52",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
+      "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ=="
+    },
+    "node_modules/gerber-to-svg": {
+      "version": "4.2.8",
+      "resolved": "https://registry.npmjs.org/gerber-to-svg/-/gerber-to-svg-4.2.8.tgz",
+      "integrity": "sha512-EgUR5cJu5s1UgB2jy+we9UBGH5c2ecmcsBsv2AOEjeYRUijMx7jg7mCsHAuGoF8ogmBWmh6/ahDxDJe7d7zzIw==",
+      "dependencies": {
+        "@tracespace/xml-id": "^4.2.7",
+        "@types/node": "^13.1.6",
+        "escape-html": "^1.0.3",
+        "gerber-parser": "^4.2.7",
+        "gerber-plotter": "^4.2.8",
+        "inherits": "^2.0.4",
+        "lodash.isfinite": "^3.3.2",
+        "readable-stream": "^3.4.0",
+        "xml-element-string": "^1.0.0"
+      }
+    },
+    "node_modules/gerber-to-svg/node_modules/@types/node": {
+      "version": "13.13.52",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
+      "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ=="
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+      "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-proto": "^1.0.1",
+        "has-symbols": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+      "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+      "dependencies": {
+        "get-intrinsic": "^1.1.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz",
+      "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/has-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+      "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+      "dependencies": {
+        "has-symbols": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "node_modules/is-arguments": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-callable": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-generator-function": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+      "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-typed-array": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
+      "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
+      "dependencies": {
+        "which-typed-array": "^1.1.11"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/lodash.fill": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.fill/-/lodash.fill-3.4.0.tgz",
+      "integrity": "sha512-YgunwHKIxPWOe3VnM65J3oi6oShakIxdLMeIZ9xxcsMxc8X/FQC2VlA4eJzMv+7GlC5gebQLn+U+qcNoG18iLA=="
+    },
+    "node_modules/lodash.isfinite": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz",
+      "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA=="
+    },
+    "node_modules/lodash.isfunction": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+      "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
+    },
+    "node_modules/lodash.padend": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
+      "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw=="
+    },
+    "node_modules/lodash.padstart": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz",
+      "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw=="
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+      "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+      "dev": true
+    },
+    "node_modules/postcss": {
+      "version": "8.4.31",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+      "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.6",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "3.29.4",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
+      "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
+      "dev": true,
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=14.18.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/save-svg-as-png": {
+      "version": "1.4.17",
+      "resolved": "https://registry.npmjs.org/save-svg-as-png/-/save-svg-as-png-1.4.17.tgz",
+      "integrity": "sha512-7QDaqJsVhdFPwviCxkgHiGm9omeaMBe1VKbHySWU6oFB2LtnGCcYS13eVoslUgq6VZC6Tjq/HddBd1K6p2PGpA=="
+    },
+    "node_modules/source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/stream-browserify": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
+      "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==",
+      "dependencies": {
+        "inherits": "~2.0.4",
+        "readable-stream": "^3.5.0"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+      "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+      "dev": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "5.25.3",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz",
+      "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==",
+      "dev": true,
+      "optional": true,
+      "peer": true
+    },
+    "node_modules/util": {
+      "version": "0.12.5",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+      "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "is-arguments": "^1.0.4",
+        "is-generator-function": "^1.0.7",
+        "is-typed-array": "^1.1.3",
+        "which-typed-array": "^1.1.2"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+    },
+    "node_modules/vite": {
+      "version": "4.4.11",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz",
+      "integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "^0.18.10",
+        "postcss": "^8.4.27",
+        "rollup": "^3.27.1"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      },
+      "peerDependencies": {
+        "@types/node": ">= 14",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/which-typed-array": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz",
+      "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
+      "dependencies": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/xml-element-string": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/xml-element-string/-/xml-element-string-1.0.0.tgz",
+      "integrity": "sha512-JhO/ZCCwce8c9rLVXEA/KAy3Kg5340Ey4QBglQ+9ScQrQdroothAXblTRtyGkJZRRqQkkJSMZx8Kd7DGPMBsAw==",
+      "dependencies": {
+        "escape-html": "^1.0.3"
+      }
+    }
+  },
+  "dependencies": {
+    "@esbuild/android-arm": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
+      "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
+      "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
+      "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/darwin-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
+      "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/darwin-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
+      "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/freebsd-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
+      "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/freebsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
+      "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-arm": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
+      "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
+      "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-ia32": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
+      "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-loong64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
+      "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-mips64el": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
+      "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-ppc64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
+      "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-riscv64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
+      "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-s390x": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
+      "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
+      "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/netbsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
+      "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/openbsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
+      "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/sunos-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
+      "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
+      "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-ia32": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
+      "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+      "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@tracespace/xml-id": {
+      "version": "4.2.7",
+      "resolved": "https://registry.npmjs.org/@tracespace/xml-id/-/xml-id-4.2.7.tgz",
+      "integrity": "sha512-4T7uAx5HB6qwKNH7jQDiij4EDe+uP+zlFcccMpZageZA5S2V2GFxm3g5lThJdTaQhp8Or4FLsmu8C/BRdYS7hg=="
+    },
+    "@types/node": {
+      "version": "20.8.4",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz",
+      "integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==",
+      "dev": true,
+      "optional": true,
+      "peer": true,
+      "requires": {
+        "undici-types": "~5.25.1"
+      }
+    },
+    "available-typed-arrays": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+      "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
+    },
+    "base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+    },
+    "browserify-zlib": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+      "requires": {
+        "pako": "~1.0.5"
+      }
+    },
+    "buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "requires": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "requires": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      }
+    },
+    "esbuild": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
+      "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+      "dev": true,
+      "requires": {
+        "@esbuild/android-arm": "0.18.20",
+        "@esbuild/android-arm64": "0.18.20",
+        "@esbuild/android-x64": "0.18.20",
+        "@esbuild/darwin-arm64": "0.18.20",
+        "@esbuild/darwin-x64": "0.18.20",
+        "@esbuild/freebsd-arm64": "0.18.20",
+        "@esbuild/freebsd-x64": "0.18.20",
+        "@esbuild/linux-arm": "0.18.20",
+        "@esbuild/linux-arm64": "0.18.20",
+        "@esbuild/linux-ia32": "0.18.20",
+        "@esbuild/linux-loong64": "0.18.20",
+        "@esbuild/linux-mips64el": "0.18.20",
+        "@esbuild/linux-ppc64": "0.18.20",
+        "@esbuild/linux-riscv64": "0.18.20",
+        "@esbuild/linux-s390x": "0.18.20",
+        "@esbuild/linux-x64": "0.18.20",
+        "@esbuild/netbsd-x64": "0.18.20",
+        "@esbuild/openbsd-x64": "0.18.20",
+        "@esbuild/sunos-x64": "0.18.20",
+        "@esbuild/win32-arm64": "0.18.20",
+        "@esbuild/win32-ia32": "0.18.20",
+        "@esbuild/win32-x64": "0.18.20"
+      }
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+    },
+    "events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
+    },
+    "for-each": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+      "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+      "requires": {
+        "is-callable": "^1.1.3"
+      }
+    },
+    "fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "optional": true
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "gerber-parser": {
+      "version": "4.2.7",
+      "resolved": "https://registry.npmjs.org/gerber-parser/-/gerber-parser-4.2.7.tgz",
+      "integrity": "sha512-Docb3egdLPjqYu/oH9Zhhc25kb2Ub9qkVFXu3seg13nDNW+KxbglqvyDcC0CGxkXxCaTqP3iyWNBG38fssmGKw==",
+      "requires": {
+        "@types/node": "^13.1.6",
+        "inherits": "^2.0.4",
+        "lodash.isfinite": "^3.3.2",
+        "lodash.padend": "^4.6.1",
+        "lodash.padstart": "^4.6.1",
+        "readable-stream": "^3.4.0"
+      },
+      "dependencies": {
+        "@types/node": {
+          "version": "13.13.52",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
+          "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ=="
+        }
+      }
+    },
+    "gerber-plotter": {
+      "version": "4.2.8",
+      "resolved": "https://registry.npmjs.org/gerber-plotter/-/gerber-plotter-4.2.8.tgz",
+      "integrity": "sha512-n+Kg4HJQzCVBvgh73Rit8xCrQOJFhnnIBV7psboURjlcIwuYum4eAEOTd6S3GyXXvxGGay3aOmp6HwQ373ciuQ==",
+      "requires": {
+        "@types/node": "^13.1.6",
+        "inherits": "^2.0.4",
+        "lodash.fill": "^3.4.0",
+        "lodash.isfinite": "^3.3.2",
+        "lodash.isfunction": "^3.0.9",
+        "readable-stream": "^3.4.0"
+      },
+      "dependencies": {
+        "@types/node": {
+          "version": "13.13.52",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
+          "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ=="
+        }
+      }
+    },
+    "gerber-to-svg": {
+      "version": "4.2.8",
+      "resolved": "https://registry.npmjs.org/gerber-to-svg/-/gerber-to-svg-4.2.8.tgz",
+      "integrity": "sha512-EgUR5cJu5s1UgB2jy+we9UBGH5c2ecmcsBsv2AOEjeYRUijMx7jg7mCsHAuGoF8ogmBWmh6/ahDxDJe7d7zzIw==",
+      "requires": {
+        "@tracespace/xml-id": "^4.2.7",
+        "@types/node": "^13.1.6",
+        "escape-html": "^1.0.3",
+        "gerber-parser": "^4.2.7",
+        "gerber-plotter": "^4.2.8",
+        "inherits": "^2.0.4",
+        "lodash.isfinite": "^3.3.2",
+        "readable-stream": "^3.4.0",
+        "xml-element-string": "^1.0.0"
+      },
+      "dependencies": {
+        "@types/node": {
+          "version": "13.13.52",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
+          "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ=="
+        }
+      }
+    },
+    "get-intrinsic": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+      "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
+      "requires": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-proto": "^1.0.1",
+        "has-symbols": "^1.0.3"
+      }
+    },
+    "gopd": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+      "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+      "requires": {
+        "get-intrinsic": "^1.1.3"
+      }
+    },
+    "has": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz",
+      "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ=="
+    },
+    "has-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+      "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="
+    },
+    "has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+    },
+    "has-tostringtag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+      "requires": {
+        "has-symbols": "^1.0.2"
+      }
+    },
+    "ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "is-arguments": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+      "requires": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      }
+    },
+    "is-callable": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="
+    },
+    "is-generator-function": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+      "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+      "requires": {
+        "has-tostringtag": "^1.0.0"
+      }
+    },
+    "is-typed-array": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
+      "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
+      "requires": {
+        "which-typed-array": "^1.1.11"
+      }
+    },
+    "lodash.fill": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.fill/-/lodash.fill-3.4.0.tgz",
+      "integrity": "sha512-YgunwHKIxPWOe3VnM65J3oi6oShakIxdLMeIZ9xxcsMxc8X/FQC2VlA4eJzMv+7GlC5gebQLn+U+qcNoG18iLA=="
+    },
+    "lodash.isfinite": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz",
+      "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA=="
+    },
+    "lodash.isfunction": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+      "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
+    },
+    "lodash.padend": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
+      "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw=="
+    },
+    "lodash.padstart": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz",
+      "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw=="
+    },
+    "nanoid": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+      "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+      "dev": true
+    },
+    "pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
+    "picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+      "dev": true
+    },
+    "postcss": {
+      "version": "8.4.31",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+      "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+      "dev": true,
+      "requires": {
+        "nanoid": "^3.3.6",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
+    },
+    "readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "requires": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      }
+    },
+    "rollup": {
+      "version": "3.29.4",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
+      "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
+      "dev": true,
+      "requires": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+    },
+    "save-svg-as-png": {
+      "version": "1.4.17",
+      "resolved": "https://registry.npmjs.org/save-svg-as-png/-/save-svg-as-png-1.4.17.tgz",
+      "integrity": "sha512-7QDaqJsVhdFPwviCxkgHiGm9omeaMBe1VKbHySWU6oFB2LtnGCcYS13eVoslUgq6VZC6Tjq/HddBd1K6p2PGpA=="
+    },
+    "source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "dev": true
+    },
+    "stream-browserify": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
+      "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==",
+      "requires": {
+        "inherits": "~2.0.4",
+        "readable-stream": "^3.5.0"
+      }
+    },
+    "string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "requires": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "typescript": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+      "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+      "dev": true
+    },
+    "undici-types": {
+      "version": "5.25.3",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz",
+      "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==",
+      "dev": true,
+      "optional": true,
+      "peer": true
+    },
+    "util": {
+      "version": "0.12.5",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+      "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+      "requires": {
+        "inherits": "^2.0.3",
+        "is-arguments": "^1.0.4",
+        "is-generator-function": "^1.0.7",
+        "is-typed-array": "^1.1.3",
+        "which-typed-array": "^1.1.2"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+    },
+    "vite": {
+      "version": "4.4.11",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz",
+      "integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
+      "dev": true,
+      "requires": {
+        "esbuild": "^0.18.10",
+        "fsevents": "~2.3.2",
+        "postcss": "^8.4.27",
+        "rollup": "^3.27.1"
+      }
+    },
+    "which-typed-array": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz",
+      "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
+      "requires": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "has-tostringtag": "^1.0.0"
+      }
+    },
+    "xml-element-string": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/xml-element-string/-/xml-element-string-1.0.0.tgz",
+      "integrity": "sha512-JhO/ZCCwce8c9rLVXEA/KAy3Kg5340Ey4QBglQ+9ScQrQdroothAXblTRtyGkJZRRqQkkJSMZx8Kd7DGPMBsAw==",
+      "requires": {
+        "escape-html": "^1.0.3"
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..956a3bd
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+  "name": "gerber2png",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "tsc && vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "buffer": "^6.0.0",
+    "events": "^3.0.0",
+    "gerber-to-svg": "^4.2.8",
+    "save-svg-as-png": "^1.4.0",
+    "browserify-zlib": "^0.2.0",
+    "process": "^0.11.10",
+    "stream-browserify": "^3.0.0",
+    "util": "^0.12.5"
+  },
+  "devDependencies": {
+    "typescript": "^5.0.2",
+    "vite": "^4.4.11"
+  }
+}
diff --git a/src/interface.js b/src/interface.js
new file mode 100644
index 0000000..452fad7
--- /dev/null
+++ b/src/interface.js
@@ -0,0 +1,464 @@
+// Quentin Bolsee and Jake Read, MIT Center for Bits and Atoms, 2023
+// import * as saveSvgAsPng from 'save-svg-as-png'
+// import * as downloadAsPng from 'save-svg-as-png'
+// require('save-svg-as-png')
+// require('process');
+import process from "process";
+// import { util } from "util";
+import { Buffer } from "buffer";
+import EventEmitter from "events";
+
+window.Buffer = Buffer;
+window.process = process;
+window.EventEmitter = EventEmitter;
+
+import gerberToSvg from 'gerber-to-svg'
+import * as saveSvg from 'save-svg-as-png'
+
+let sourceElem = document.getElementById("body");
+let svgElem = document.getElementById("previewSVG");
+
+// order of known layers, KiCAD and EAGLE namings
+const lookupOrder = [
+    "Edge_Cuts", // KiCAD
+    "profile",   // EAGLE
+    "B_Cu",
+    "copper_bottom",
+    "F_Cu",
+    "copper_top",
+    "B_Mask",
+    "soldermask_bottom",
+    "F_Mask",
+    "soldermask_top",
+    "B_Paste",
+    "solderpaste_bottom",
+    "F_Paste",
+    "solderpaste_top",
+    "drill",
+    "B_Silkscreen",
+    "B_Silks",
+    "silkscreen_bottom",
+    "F_Silkscreen",
+    "F_Silks",
+    "silkscreen_top",
+];
+
+const lookupColor = {
+    "Edge_Cuts": [70,105,58,1.0],
+    "profile": [70,105,58,1.0],
+    "B_Cu": [30,130,220,0.8],
+    "copper_bottom": [30,130,220,0.8],
+    "F_Cu": [89,165,82,0.8],
+    "copper_top": [89,165,82,0.8],
+    "B_Mask": [200,177,170,0.6],
+    "soldermask_bottom": [200,177,170,0.6],
+    "F_Mask": [239,177,58,1.0],
+    "soldermask_top": [239,177,58,1.0],
+    "B_Paste": [200,20,230,0.2],
+    "solderpaste_bottom": [200,20,230,0.2],
+    "F_Paste": [200,20,230,0.3],
+    "solderpaste_top": [200,20,230,0.3],
+    "drill": [120,120,120,1.0],
+    "B_Silkscreen": [255,255,255,0.6],
+    "B_Silks": [255,255,255,0.6],
+    "silkscreen_bottom": [255,255,255,0.6],
+    "F_Silkscreen": [255,255,255,1.0],
+    "F_Silks": [255,255,255,1.0],
+    "silkscreen_top": [255,255,255,1.0],
+};
+
+let globalLayers = [];
+
+let globalXML = null;
+
+// units in mm
+let globalSettings = {
+    fillEdge : true,
+    asSVG: false,
+    dpi: 1000,
+    svgOrig : [NaN, NaN],
+    lockOrig : false,
+    svgDim : [NaN, NaN],
+    lockDim : false,
+    svgMargin : [0, 0],
+};
+
+function renderSVG() {
+    if (globalXML === null) {
+        svgElem.outerHTML = '<svg id="previewSVG"></svg>';
+    } else {
+        let serializer = new XMLSerializer();
+        svgElem.outerHTML = serializer.serializeToString(globalXML);
+    }
+    // update reference
+    svgElem = document.getElementById("previewSVG");
+}
+
+function updateSVG(fromSettingsChange=false) {
+    let parser = new DOMParser();
+    globalXML = parser.parseFromString('<svg id="previewSVG" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>', "text/xml");
+
+    if (globalLayers.length == 0) {
+        renderSVG();
+        return;
+    }
+
+    let layersSorted = globalLayers.toSorted((a, b) => {
+        return lookupOrder.indexOf(a[0]) - lookupOrder.indexOf(b[0]);
+    });
+
+    let layerName, layerTxt;
+
+    let xMin = Number.MAX_VALUE;
+    let xMax = -Number.MAX_VALUE;
+    let yMin = Number.MAX_VALUE;
+    let yMax = -Number.MAX_VALUE;
+
+    let deltaY = [];
+
+    // reset XML
+    let globalXMLRoot = globalXML.childNodes[0];
+    globalXMLRoot.setAttribute("stroke-linecap", "round");
+    globalXMLRoot.setAttribute("stroke-linejoin", "round");
+    globalXMLRoot.setAttribute("stroke-width", "0");
+    // globalXMLRoot.setAttribute("background-color", "rgb(255,255,255)");
+    globalXMLRoot.setAttribute("fill-rule", "evenodd");
+
+    let layerVB, layerXML, layerXMLRoot;
+
+    // compute viewBox
+    for ([layerName, layerTxt] of layersSorted) {
+        layerXML = parser.parseFromString(layerTxt, "text/xml");
+        layerXMLRoot = layerXML.childNodes[0];
+        layerVB = layerXMLRoot.getAttribute("viewBox").split(" ").map((x) => Number(x));
+        if (layerXMLRoot.childNodes.length == 0) {
+            continue;
+        }
+        xMin = Math.min(xMin, layerVB[0]);
+        xMax = Math.max(xMax, layerVB[0]+layerVB[2]);
+        yMin = Math.min(yMin, layerVB[1]);
+        yMax = Math.max(yMax, layerVB[1]+layerVB[3]);
+    }
+    // sync settings
+    if (!fromSettingsChange) {
+        if (!globalSettings.lockOrig || isNaN(globalSettings.svgOrig[0])) {
+            globalSettings.svgOrig[0] = xMin / 1000.0;
+            globalSettings.svgOrig[1] = yMin / 1000.0;
+        }
+        if (!globalSettings.lockDim || isNaN(globalSettings.svgDim[0])) {
+            globalSettings.svgDim[0] = (xMax - xMin) / 1000.0;
+            globalSettings.svgDim[1] = (yMax - yMin) / 1000.0;
+        }
+    }
+    writeSettings();
+    let vb = [
+        (globalSettings.svgOrig[0]-globalSettings.svgMargin[0]),
+        (globalSettings.svgOrig[1]-globalSettings.svgMargin[1]),
+        (globalSettings.svgDim[0]+2*globalSettings.svgMargin[0]),
+        (globalSettings.svgDim[1]+2*globalSettings.svgMargin[1])
+    ].map((x) => x * 1000);
+
+    globalXMLRoot.setAttribute("viewBox", vb.join(' '));
+    globalXMLRoot.setAttribute("width", `${(vb[2]/1000).toFixed(3)}mm`);
+    globalXMLRoot.setAttribute("height", `${(vb[3]/1000).toFixed(3)}mm`);
+
+    for ([layerName, layerTxt] of layersSorted) {
+        layerXML = parser.parseFromString(layerTxt, "text/xml");
+        layerXMLRoot = layerXML.childNodes[0];
+        layerVB = layerXMLRoot.getAttribute("viewBox").split(" ").map((x) => Number(x));
+
+        let layerRGB = 'rgb('+lookupColor[layerName].slice(0, 3).join(',')+')';
+        let layerOpacity = lookupColor[layerName][3];
+
+        for (let g of layerXMLRoot.childNodes) {
+            if (g.tagName == "g") {
+                const regex_translate = /translate\([-.\d]+,\s*([-.\d]+)\)/;
+                const m = g.getAttribute("transform").match(regex_translate);
+                // fix vertical alignment (none of this makes sense)
+                let ty = Number(m[1]);
+                ty += ((vb[1] + vb[3]) - (layerVB[1] + layerVB[3]));
+                ty += (vb[1] - layerVB[1]);
+                g.setAttribute("transform", `translate(0, ${ty}) scale(1, -1)`);
+
+                // let ty = Number(m[1]) + (layerVB[3]-(yMax-yMin))/2;
+                g.setAttribute("opacity", layerOpacity);
+                g.setAttribute("fill", layerRGB);
+                g.setAttribute("stroke", layerRGB);
+                g.childNodes.forEach((p) => {
+                    if (["profile", "Edge_Cuts"].includes(layerName) && globalSettings.fillEdge) {
+                        // fill mode
+                        p.removeAttribute("fill");
+                        p.setAttribute("stroke", "none");
+                    }
+                });
+            }
+            globalXMLRoot.appendChild(g.cloneNode(true));
+        }
+    }
+
+    renderSVG();
+}
+
+function setDPI(canvas, dpi) {
+    // Set up CSS size.
+    canvas.style.width = canvas.style.width || canvas.width + 'px';
+    canvas.style.height = canvas.style.height || canvas.height + 'px';
+
+    // Resize canvas and scale future draws.
+    var scaleFactor = dpi / 96;
+    canvas.width = Math.ceil(canvas.width * scaleFactor);
+    canvas.height = Math.ceil(canvas.height * scaleFactor);
+    var ctx = canvas.getContext('2d');
+    ctx.scale(scaleFactor, scaleFactor);
+}
+
+function downloadImage(separateLayers=false) {
+    if (separateLayers) {
+        return;
+    }
+
+    saveSvg.saveSvgAsPng(svgElem, "diagram.png");
+    // if (isNaN(globalSettings.svgDim[0])) {
+    //     return;
+    // }
+    // let svg = svgElem.closest('svg');
+    // let svgWidth = svg.attributes['width'].value;
+    // let svgHeight = svg.attributes['height'].value;
+    //
+    // let mmWidth = (globalSettings.svgDim[0]+2*globalSettings.svgMargin[0]);
+    // let mmHeight = (globalSettings.svgDim[1]+2*globalSettings.svgMargin[1]);
+    // let pxWidth = Math.round(globalSettings.dpi * mmWidth / 25.4);
+    // let pxHeight = Math.round(globalSettings.dpi * mmHeight / 25.4);
+    //
+    // let image = new Image();
+    // let canvas = document.getElementById('canvas');
+    // setDPI(canvas, globalSettings.dpi);
+    // // canvas.width = pxWidth;
+    // // canvas.height = pxHeight;
+    // let ctx = canvas.getContext('2d');
+    //
+    // image.addEventListener('load', (e) => {
+    //     console.log(e.target.width)
+    //     console.log(e.target.height)
+    //     ctx.drawImage(e.target, 0, 0, e.target.width, e.target.height);
+    //     const a = document.createElement('a');
+    //     a.style.display = 'none';
+    //     a.href = canvas.toDataURL("image/png");
+    //     a.download = 'render.png';
+    //     document.body.appendChild(a);
+    //     a.click();
+    // });
+    // let svgURL = new XMLSerializer().serializeToString(svgElem);
+    // image.src = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgURL);
+    // let txt = JSON.stringify(layers);
+    // var txtBlob = new Blob([txt], {
+    //     type: 'text/plain'
+    // });
+    // const url = window.URL.createObjectURL(txtBlob);
+    // // the filename you want
+    // window.URL.revokeObjectURL(url);
+    //
+    // console.log(JSON.parse(txt))
+}
+
+var dropZone = document.getElementById('dropZone');
+
+function showDropZone() {
+    dropZone.style.visibility = "visible";
+}
+
+function hideDropZone() {
+    dropZone.style.visibility = "hidden";
+}
+
+window.addEventListener('dragenter', (e) => {
+    showDropZone();
+});
+
+function allowDrag(e) {
+    if (true) {  // Test that the item being dragged is a valid one
+        e.dataTransfer.dropEffect = 'copy';
+        e.preventDefault();
+    }
+}
+
+dropZone.addEventListener('dragenter', allowDrag);
+dropZone.addEventListener('dragover', allowDrag);
+
+dropZone.addEventListener('dragleave', (e) => {
+    hideDropZone();
+});
+
+dropZone.addEventListener('drop', (e) => {
+    e.preventDefault();
+    hideDropZone();
+    if (!e.dataTransfer.items) {
+        return;
+    }
+
+    // layerName: SVG
+    let promiseAllSVG = [];
+
+    for (let file of e.dataTransfer.files) {
+        let layerName;
+        if (file.name.endsWith('.xln') || file.name.endsWith('.drl')) {
+            layerName = "drill";
+        } else {
+            if (!file.name.endsWith('.gbr')) {
+                continue;
+            }
+            let filename = file.name.replace('.gbr', '');
+            let layerNum = -1;
+
+            for (let j in lookupOrder) {
+                layerName = lookupOrder[j];
+                if (filename.endsWith(layerName)) {
+                    layerNum = j;
+                    break;
+                }
+            }
+            if (layerNum == -1) {
+                continue;
+            }
+        }
+
+        let promiseSVG = new Promise((resolve, reject) => {
+            const reader = new FileReader();
+            reader.onload = (event) => {
+                const options = {
+                    encoding: 'utf8',
+                    optimizePaths: true
+                };
+                gerberToSvg(reader.result, options, (err, svg) => {
+                    if (err) {
+                        resolve(null);
+                    } else {
+                        resolve([layerName, svg]);
+                    }
+                });
+            }
+            reader.onerror = reject;
+            reader.readAsText(file);
+        });
+        promiseAllSVG.push(promiseSVG);
+    }
+    Promise.all(promiseAllSVG).then((values) => {
+        globalLayers = values;
+        updateSVG();
+    })
+});
+
+function readSettings() {
+    let settingsFill = document.getElementById("settingsFill");
+    let settingsAsSVG = document.getElementById("settingsAsSVG");
+    let settingsDPI = document.getElementById("settingsDPI");
+    let settingsOrigX = document.getElementById("settingsOrigX");
+    let settingsOrigY = document.getElementById("settingsOrigY");
+    let settingsDimX = document.getElementById("settingsDimX");
+    let settingsDimY = document.getElementById("settingsDimY");
+    let settingsMarginX = document.getElementById("settingsMarginX");
+    let settingsMarginY = document.getElementById("settingsMarginY");
+
+    globalSettings.fillEdge = settingsFill.checked;
+    globalSettings.asSVG = settingsAsSVG.checked;
+    globalSettings.dpi = Number(settingsDPI.value);
+    globalSettings.svgMargin = [
+        Number(settingsMarginX.value),
+        Number(settingsMarginY.value)
+    ]
+    globalSettings.svgOrig = [
+        Number(settingsOrigX.value),
+        Number(settingsOrigY.value)
+    ];
+    globalSettings.svgDim = [
+        Number(settingsDimX.value),
+        Number(settingsDimY.value)
+    ];
+}
+
+function writeSettings() {
+    let settingsOrigX = document.getElementById("settingsOrigX");
+    let settingsOrigY = document.getElementById("settingsOrigY");
+    let settingsDimX = document.getElementById("settingsDimX");
+    let settingsDimY = document.getElementById("settingsDimY");
+
+    settingsOrigX.value = globalSettings.svgOrig[0];
+    settingsOrigY.value = globalSettings.svgOrig[1];
+    settingsDimX.value = globalSettings.svgDim[0];
+    settingsDimY.value = globalSettings.svgDim[1];
+}
+
+/* buttons */
+function initListeners() {
+    // document.getElementById("loadButton").addEventListener("click", () => {
+    //     document.getElementById("fileInput").click();
+    // });
+
+    for (let elementId of [
+        'settingsFill',
+        'settingsAsSVG',
+        'settingsDPI',
+        'settingsOrigX',
+        'settingsOrigY',
+        'settingsDimX',
+        'settingsDimY',
+        'settingsMarginX',
+        'settingsMarginY']) {
+        document.getElementById(elementId).addEventListener("change", () => {
+            readSettings();
+            updateSVG(true);
+        })
+    }
+}
+
+let lockOrigElem = document.getElementById("settingsLockOrig");
+let lockDimElem = document.getElementById("settingsLockDim");
+
+lockOrigElem.addEventListener("click", () => {
+    let settingsOrigX = document.getElementById("settingsOrigX");
+    let settingsOrigY = document.getElementById("settingsOrigY");
+    if (globalSettings.lockOrig) {
+        settingsOrigX.disabled = false;
+        settingsOrigY.disabled = false;
+        globalSettings.lockOrig = false;
+        lockOrigElem.innerHTML = "🔓";
+    } else {
+        settingsOrigX.disabled = true;
+        settingsOrigY.disabled = true;
+        globalSettings.lockOrig = true;
+        lockOrigElem.innerHTML = "🔒";
+    }
+    updateSVG();
+});
+
+lockDimElem.addEventListener("click", () => {
+    let settingsDimX = document.getElementById("settingsDimX");
+    let settingsDimY = document.getElementById("settingsDimY");
+    if (globalSettings.lockDim) {
+        settingsDimX.disabled = false;
+        settingsDimY.disabled = false;
+        globalSettings.lockDim = false;
+        lockDimElem.innerHTML = "🔓";
+    } else {
+        settingsDimX.disabled = true;
+        settingsDimY.disabled = true;
+        globalSettings.lockDim = true;
+        lockDimElem.innerHTML = "🔒";
+    }
+    updateSVG();
+});
+
+document.getElementById("downloadRenderButton").addEventListener("click", () => {
+    downloadImage(false);
+});
+
+document.getElementById("downloadlayersButton").addEventListener("click", () => {
+    downloadImage(true);
+});
+
+readSettings();
+initListeners();
+
+globalLayers = JSON.parse('[["F_Cu","<svg version=\\"1.1\\" xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" stroke-width=\\"0\\" fill-rule=\\"evenodd\\" width=\\"32.531mm\\" height=\\"34.7mm\\" viewBox=\\"53775 -68780 32531 34700\\"><defs><rect x=\\"-635\\" y=\\"-1270\\" width=\\"1270\\" height=\\"2540\\" id=\\"M7zzQZkyObU1_pad-10\\"/><rect x=\\"-850\\" y=\\"-1000\\" width=\\"1700\\" height=\\"2000\\" id=\\"M7zzQZkyObU1_pad-11\\"/><rect x=\\"-1000\\" y=\\"-850\\" width=\\"2000\\" height=\\"1700\\" id=\\"M7zzQZkyObU1_pad-12\\"/><rect x=\\"-1500\\" y=\\"-800\\" width=\\"3000\\" height=\\"1600\\" id=\\"M7zzQZkyObU1_pad-13\\"/><rect x=\\"-600\\" y=\\"-1200\\" width=\\"1200\\" height=\\"2400\\" id=\\"M7zzQZkyObU1_pad-14\\"/><circle cx=\\"0\\" cy=\\"0\\" r=\\"600\\" id=\\"M7zzQZkyObU1_pad-15\\"/><rect x=\\"-370\\" y=\\"-1200\\" width=\\"740\\" height=\\"2400\\" id=\\"M7zzQZkyObU1_pad-16\\"/></defs><g transform=\\"translate(0,-102860) scale(1,-1)\\" fill=\\"currentColor\\" stroke=\\"currentColor\\"><use xlink:href=\\"#M7zzQZkyObU1_pad-10\\" x=\\"67970\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-10\\" x=\\"65430\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-10\\" x=\\"62890\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-10\\" x=\\"60350\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-10\\" x=\\"57810\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-10\\" x=\\"55270\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-11\\" x=\\"82550\\" y=\\"-48800\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-11\\" x=\\"82550\\" y=\\"-52800\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-12\\" x=\\"81375\\" y=\\"-55880\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-12\\" x=\\"77375\\" y=\\"-55880\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-12\\" x=\\"63595\\" y=\\"-55880\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-12\\" x=\\"59595\\" y=\\"-55880\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-12\\" x=\\"71215\\" y=\\"-55880\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-12\\" x=\\"67215\\" y=\\"-55880\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"61235\\" y=\\"-35560\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"61235\\" y=\\"-38100\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"61235\\" y=\\"-40640\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"61235\\" y=\\"-43180\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"61235\\" y=\\"-45720\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"61235\\" y=\\"-48260\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"61235\\" y=\\"-50800\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"78470\\" y=\\"-50800\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"78470\\" y=\\"-48260\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"78470\\" y=\\"-45720\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"78470\\" y=\\"-43180\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"78470\\" y=\\"-40640\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"78470\\" y=\\"-38100\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-13\\" x=\\"78470\\" y=\\"-35560\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-14\\" x=\\"71125\\" y=\\"-52070\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-14\\" x=\\"68585\\" y=\\"-52070\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-15\\" x=\\"68580\\" y=\\"-34680\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-15\\" x=\\"68580\\" y=\\"-37220\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-15\\" x=\\"71120\\" y=\\"-37220\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-15\\" x=\\"71120\\" y=\\"-34680\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-11\\" x=\\"57785\\" y=\\"-47720\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-11\\" x=\\"57785\\" y=\\"-43720\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-16\\" x=\\"81915\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-16\\" x=\\"81915\\" y=\\"-65450\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-16\\" x=\\"80645\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-16\\" x=\\"80645\\" y=\\"-65450\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-16\\" x=\\"79375\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-16\\" x=\\"79375\\" y=\\"-65450\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-16\\" x=\\"78105\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-16\\" x=\\"78105\\" y=\\"-65450\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-16\\" x=\\"76835\\" y=\\"-61550\\"/><use xlink:href=\\"#M7zzQZkyObU1_pad-16\\" x=\\"76835\\" y=\\"-65450\\"/><path d=\\"M 77765 -67310 78105 -66970 78105 -65450 79375 -65450 79375 -67050 79375 -66720 M 79375 -67050 79635 -67310 81280 -67310 81915 -66675 81915 -65450 83140 -65450 85090 -63500 85090 -48895 84995 -48800 82550 -48800 M 85090 -48895 85090 -42545 80645 -38100 77470 -38100 79450 -38100 M 56515 -67310 71120 -67310 77765 -67310 M 71215 -67215 71120 -67310 M 55270 -60915 55270 -61550 55270 -66065 56515 -67310 M 71215 -55880 71215 -67215 M 81915 -68580 86106 -64389 86106 -41910 79756 -35560 77470 -35560 79450 -35560 M 60039.002 -59404.002 54260.998 -59404.002 53975 -59690 53975 -66675 55880 -68580 81915 -68580 M 60350 -59715 60039.002 -59404.002 M 60350 -61550 60350 -59715 M 81915 -59690 81915 -61550 M 77470 -48260 74930 -50800 74930 -56504.295 76845.206 -58419.501 80644.501 -58419.501 81915 -59690 M 74120.499 -49069.501 74120.499 -56839.602 76509.9 -59229.002 77384.002 -59229.002 78105 -59950 78105 -61550 M 77470 -45720 74120.499 -49069.501 M 76835 -60960 73310.998 -57435.998 73310.998 -47339.002 77470 -43180 M 76835 -61550 76835 -60960 M 56515 -56385.488 57914.512 -57785 62300 -57785 65430 -60915 M 56515 -48990 56515 -56385.488 M 57785 -47720 56515 -48990 M 57229.704 -58245 57229 -58245 55372 -56388 55372 -43053 57785 -40640 62235 -40640 M 61769.501 -58594.501 57579.205 -58594.501 57229.704 -58245 M 62890 -59715 61769.501 -58594.501 M 62890 -60915 62890 -59715 M 62235 -48392 62235 -48260 M 58325 -43180 57785 -43720 M 62235 -43180 58325 -43180 M 59595 -51530 59595 -55880 M 60325 -50800 59595 -51530 M 62235 -50800 60325 -50800 M 63595 -55880 67215 -55880 M 77375 -50895 77375 -55880 M 79375 -50800 77470 -50800 77375 -50895 M 81375 -55880 81915 -55880 82550 -55245 82550 -52800\\" fill=\\"none\\" stroke-width=\\"400\\"/></g></svg>"],["Edge_Cuts","<svg version=\\"1.1\\" xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" stroke-width=\\"0\\" fill-rule=\\"evenodd\\" width=\\"34.39mm\\" height=\\"38.835mm\\" viewBox=\\"52655 -71170 34390 38835\\"><g transform=\\"translate(0,-103505) scale(1,-1)\\" fill=\\"currentColor\\" stroke=\\"currentColor\\"><path d=\\"M 86995 -34925 A 2540 2540 0 0 1 84455 -32385 L 55245 -32385 A 2540 2540 0 0 1 52705 -34925 L 52705 -68580 A 2540 2540 0 0 1 55245 -71120 L 84455 -71120 A 2540 2540 0 0 1 86995 -68580 L 86995 -34925\\" fill=\\"none\\" stroke-width=\\"100\\"/></g></svg>"]]');
+
+updateSVG();
diff --git a/src/style.css b/src/style.css
new file mode 100644
index 0000000..c7b8476
--- /dev/null
+++ b/src/style.css
@@ -0,0 +1,136 @@
+body {
+    font-family: Arial, Helvetica, sans-serif;
+}
+
+input {
+    max-width: 30%;
+}
+
+p {
+    color: rgba(0, 0, 0, 1);
+}
+
+h3 {
+    margin-left: 4px;
+    margin-top: 20px;
+    margin-bottom: 4px;
+}
+
+p.lock {
+    cursor: default;
+    margin: 0;
+}
+
+p#dropText {
+    font-size: 100px;
+}
+
+/* #paper and #controls should be inline with each other in a 60 40 ratio*/
+.container {
+    display: flex;
+}
+
+/* input {
+    border-radius: 2px;
+} */
+
+button {
+    border: none;
+}
+
+button.interface {
+    background-color: rgb(200, 200, 200);
+    padding: 4px;
+    border-radius: 6px;
+    margin: 4px;
+    width: 28%;
+    min-width: 80px;
+    /* font-size: 10px; */
+}
+
+button.interface:hover {
+    background-color: rgb(160, 160, 160);
+    cursor: pointer;
+}
+
+button.lock {
+    background: none;
+    border: none;
+    margin: 0;
+    padding: 0;
+    cursor: pointer;
+}
+
+div {
+    margin: 4px;
+}
+
+h2 {
+    margin: 4px;
+}
+
+/* button.settingsButton {
+    width: 30%;
+} */
+
+
+.info {
+    background-color: rgb(221, 221, 221);
+    padding: 5px;
+    border-radius: 8px;
+}
+
+.full-flex {
+    flex: 1;
+}
+
+.panel {
+    display: flex;
+    flex-direction: column;
+    padding: 4px;
+}
+
+.settings {
+    width: 35vh;
+    height: 65vh;
+    margin: 4px;
+    display: flex;
+    border-radius: 6px;
+    border: 1px solid rgb(0, 0, 0);
+    flex-direction: column;
+}
+
+#previewSVG {
+    width: 100%;
+    height: 100%;
+}
+
+div.SVGbox {
+    border: 1px solid rgb(0, 0, 0);
+}
+
+div.preview {
+    width: 65vh;
+    height: 65vh;
+    margin: 4px;
+    border-radius: 6px;
+    background-color: rgb(255, 255, 255);
+    border: 1px solid rgb(0, 0, 0);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+div#dropZone {
+  background-color: rgba(180, 180, 180, 0.6);
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 999;
+  visibility: hidden;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1 @@
+/// <reference types="vite/client" />
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..75abdef
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,23 @@
+{
+  "compilerOptions": {
+    "target": "ES2020",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "lib": ["ES2020", "DOM", "DOM.Iterable"],
+    "skipLibCheck": true,
+
+    /* Bundler mode */
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+
+    /* Linting */
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true
+  },
+  "include": ["src"]
+}
diff --git a/vite.config.js b/vite.config.js
new file mode 100644
index 0000000..161206c
--- /dev/null
+++ b/vite.config.js
@@ -0,0 +1,21 @@
+import { resolve } from 'path'
+import { defineConfig } from 'vite'
+
+export default defineConfig({
+    resolve: {
+      alias: {
+        process: "process/browser",
+        stream: "stream-browserify",
+        zlib: "browserify-zlib",
+        util: 'util'
+      }
+    },
+    build: {
+        rollupOptions: {
+            input: {
+                main: resolve(__dirname, 'index.html'),
+                // nested: resolve(__dirname, 'nested/index.html'),
+            },
+        },
+    },
+})
-- 
GitLab