diff --git a/client/.gitignore b/client/.gitignore
new file mode 100644
index 0000000..e700210
--- /dev/null
+++ b/client/.gitignore
@@ -0,0 +1,5 @@
+.DS_Store
+node_modules
+/.svelte
+/build
+/functions
diff --git a/client/.npmrc b/client/.npmrc
new file mode 100644
index 0000000..b6f27f1
--- /dev/null
+++ b/client/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/client/.prettierignore b/client/.prettierignore
new file mode 100644
index 0000000..844cfd1
--- /dev/null
+++ b/client/.prettierignore
@@ -0,0 +1,4 @@
+.svelte/**
+static/**
+build/**
+node_modules/**
diff --git a/client/.prettierrc b/client/.prettierrc
new file mode 100644
index 0000000..fd71d0b
--- /dev/null
+++ b/client/.prettierrc
@@ -0,0 +1,7 @@
+{
+ "useTabs": true,
+ "singleQuote": true,
+ "trailingComma": "es5",
+ "printWidth": 100,
+ "semi": false
+}
diff --git a/client/README.md b/client/README.md
new file mode 100644
index 0000000..82510ca
--- /dev/null
+++ b/client/README.md
@@ -0,0 +1,38 @@
+# create-svelte
+
+Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte);
+
+## Creating a project
+
+If you're seeing this, you've probably already done this step. Congrats!
+
+```bash
+# create a new project in the current directory
+npm init svelte@next
+
+# create a new project in my-app
+npm init svelte@next my-app
+```
+
+> Note: the `@next` is temporary
+
+## Developing
+
+Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
+
+```bash
+npm run dev
+
+# or start the server and open the app in a new browser tab
+npm run dev -- --open
+```
+
+## Building
+
+Before creating a production version of your app, install an [adapter](https://kit.svelte.dev/docs#adapters) for your target environment. Then:
+
+```bash
+npm run build
+```
+
+> You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production.
diff --git a/client/package-lock.json b/client/package-lock.json
new file mode 100644
index 0000000..836d13f
--- /dev/null
+++ b/client/package-lock.json
@@ -0,0 +1,986 @@
+{
+ "name": "cryptgeon",
+ "version": "0.0.1",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "cryptgeon",
+ "version": "0.0.1",
+ "dependencies": {
+ "@fontsource/fira-mono": "^4.2.2",
+ "axios": "^0.21.1",
+ "copy-to-clipboard": "^3.3.1"
+ },
+ "devDependencies": {
+ "@sveltejs/kit": "next",
+ "svelte": "^3.34.0",
+ "svelte-preprocess": "^4.0.0",
+ "tslib": "^2.0.0",
+ "typescript": "^4.0.0",
+ "vite": "^2.1.0"
+ }
+ },
+ "node_modules/@fontsource/fira-mono": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@fontsource/fira-mono/-/fira-mono-4.2.2.tgz",
+ "integrity": "sha512-t2WRThg+eLkQNQCtPG2sCCq40lz3xeb7nsL7P8l4+wfSRbdLQXAY5IebMftI2YEZR4MRRhdgrg0p5fi/2yXypA=="
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.0.tgz",
+ "integrity": "sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==",
+ "dev": true,
+ "dependencies": {
+ "estree-walker": "^2.0.1",
+ "picomatch": "^2.2.2"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0"
+ }
+ },
+ "node_modules/@sveltejs/kit": {
+ "version": "1.0.0-next.94",
+ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.94.tgz",
+ "integrity": "sha512-2HkW+LqijRAHjKzKur3uLNblV8Ea1Vsju7LPVNYfMarJAk/mJrLTTm+29Qw6VSgaQsezlKfrgv3i+ZwgxZM1BQ==",
+ "dev": true,
+ "dependencies": {
+ "@sveltejs/vite-plugin-svelte": "^1.0.0-next.9",
+ "cheap-watch": "^1.0.3",
+ "sade": "^1.7.4"
+ },
+ "bin": {
+ "svelte-kit": "svelte-kit.js"
+ },
+ "engines": {
+ "node": ">= 12.17.0"
+ },
+ "peerDependencies": {
+ "svelte": "^3.32.1",
+ "vite": "^2.2.3"
+ }
+ },
+ "node_modules/@sveltejs/vite-plugin-svelte": {
+ "version": "1.0.0-next.9",
+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.9.tgz",
+ "integrity": "sha512-ySB/GJsZV3h3jqjq5WIiaxVFkJK6vqtG9gS7Iw6SfUH9ZiFNw5JjQF69g68j9cNep3q4yRIYiG5/pI3YIdXEuA==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^4.1.0",
+ "chalk": "^4.1.1",
+ "debug": "^4.3.2",
+ "hash-sum": "^2.0.0",
+ "require-relative": "^0.8.7",
+ "slash": "^4.0.0",
+ "source-map": "^0.7.3",
+ "svelte-hmr": "^0.14.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "svelte": "^3.37.0",
+ "vite": "^2.2.3"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "15.0.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
+ "integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA==",
+ "dev": true
+ },
+ "node_modules/@types/pug": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.4.tgz",
+ "integrity": "sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI=",
+ "dev": true
+ },
+ "node_modules/@types/sass": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.16.0.tgz",
+ "integrity": "sha512-2XZovu4NwcqmtZtsBR5XYLw18T8cBCnU2USFHTnYLLHz9fkhnoEMoDsqShJIOFsFhn5aJHjweiUUdTrDGujegA==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/axios": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
+ "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
+ "dependencies": {
+ "follow-redirects": "^1.10.0"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/cheap-watch": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cheap-watch/-/cheap-watch-1.0.3.tgz",
+ "integrity": "sha512-xC5CruMhLzjPwJ5ecUxGu1uGmwJQykUhqd2QrCrYbwvsFYdRyviu6jG9+pccwDXJR/OpmOTOJ9yLFunVgQu9wg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/colorette": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
+ "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
+ "dev": true
+ },
+ "node_modules/copy-to-clipboard": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz",
+ "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==",
+ "dependencies": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+ "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/detect-indent": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz",
+ "integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.9.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.9.7.tgz",
+ "integrity": "sha512-VtUf6aQ89VTmMLKrWHYG50uByMF4JQlVysb8dmg6cOgW8JnFCipmz7p+HNBl+RR3LLCuBxFGVauAe2wfnF9bLg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.0.tgz",
+ "integrity": "sha512-0vRwd7RKQBTt+mgu87mtYeofLFZpTas2S9zY+jIeuLJMNvudIgF52nr19q40HOwH5RrhWIPuj9puybzSJiRrVg==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "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==",
+ "dev": true
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hash-sum": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
+ "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==",
+ "dev": true
+ },
+ "node_modules/is-core-module": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz",
+ "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/min-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mri": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz",
+ "integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.1.22",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz",
+ "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==",
+ "dev": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz",
+ "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.2.13",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.13.tgz",
+ "integrity": "sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ==",
+ "dev": true,
+ "dependencies": {
+ "colorette": "^1.2.2",
+ "nanoid": "^3.1.22",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ }
+ },
+ "node_modules/postcss/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-relative": {
+ "version": "0.8.7",
+ "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
+ "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
+ "dev": true
+ },
+ "node_modules/resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.46.0.tgz",
+ "integrity": "sha512-qPGoUBNl+Z8uNu0z7pD3WPTABWRbcOwIrO/5ccDJzmrtzn0LVf6Lj91+L5CcWhXl6iWf23FQ6m8Jkl2CmN1O7Q==",
+ "dev": true,
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.1"
+ }
+ },
+ "node_modules/sade": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz",
+ "integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==",
+ "dev": true,
+ "dependencies": {
+ "mri": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/slash": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
+ "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/strip-indent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+ "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+ "dev": true,
+ "dependencies": {
+ "min-indent": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/svelte": {
+ "version": "3.38.0",
+ "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.38.0.tgz",
+ "integrity": "sha512-V0CbyzvXEka7zQtRYt++cpPh0zxxDUTIeNlq4KncGXn2qHnQFZ2i4L4+2QOfLwOudb9cewJeegupBQcscnBaaA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/svelte-hmr": {
+ "version": "0.14.3",
+ "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.3.tgz",
+ "integrity": "sha512-N56xX405zLMw2tpGHKRx5h+kmdeZwxI21pvyC6OyBHJDCF6DlwWBm9TifdQmSD4dloWSmpDPzHWYa3CSjfopUg==",
+ "dev": true,
+ "peerDependencies": {
+ "svelte": ">=3.19.0"
+ }
+ },
+ "node_modules/svelte-preprocess": {
+ "version": "4.7.2",
+ "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.7.2.tgz",
+ "integrity": "sha512-EToG+08rEsA33btv+C5g2qnRArwpTc5AoU0QBB3ZEkYagxAb2yPNsy0qsmtvbJOTBMy6o3oyijDdl3DMpMvpEg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "@types/pug": "^2.0.4",
+ "@types/sass": "^1.16.0",
+ "detect-indent": "^6.0.0",
+ "strip-indent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 9.11.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.10.2",
+ "coffeescript": "^2.5.1",
+ "less": "^3.11.3",
+ "postcss": "^7 || ^8",
+ "postcss-load-config": "^2.1.0 || ^3.0.0",
+ "pug": "^3.0.0",
+ "sass": "^1.26.8",
+ "stylus": "^0.54.7",
+ "sugarss": "^2.0.0",
+ "svelte": "^3.23.0",
+ "typescript": "^3.9.5 || ^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "coffeescript": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "node-sass": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "postcss-load-config": {
+ "optional": true
+ },
+ "pug": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
+ },
+ "node_modules/tslib": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
+ "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==",
+ "dev": true
+ },
+ "node_modules/typescript": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
+ "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-2.2.3.tgz",
+ "integrity": "sha512-PtjyBL4GtACM+uT5q5hi2+AlMBbb6YI2b2bam6QI8ZdZt4FezseF0yZHQx0G+b3po9jIJ/GS5N9gc5Yq9Rue7g==",
+ "dev": true,
+ "dependencies": {
+ "esbuild": "^0.9.3",
+ "postcss": "^8.2.1",
+ "resolve": "^1.19.0",
+ "rollup": "^2.38.5"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.1"
+ }
+ }
+ },
+ "dependencies": {
+ "@fontsource/fira-mono": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@fontsource/fira-mono/-/fira-mono-4.2.2.tgz",
+ "integrity": "sha512-t2WRThg+eLkQNQCtPG2sCCq40lz3xeb7nsL7P8l4+wfSRbdLQXAY5IebMftI2YEZR4MRRhdgrg0p5fi/2yXypA=="
+ },
+ "@rollup/pluginutils": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.0.tgz",
+ "integrity": "sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==",
+ "dev": true,
+ "requires": {
+ "estree-walker": "^2.0.1",
+ "picomatch": "^2.2.2"
+ }
+ },
+ "@sveltejs/kit": {
+ "version": "1.0.0-next.94",
+ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.94.tgz",
+ "integrity": "sha512-2HkW+LqijRAHjKzKur3uLNblV8Ea1Vsju7LPVNYfMarJAk/mJrLTTm+29Qw6VSgaQsezlKfrgv3i+ZwgxZM1BQ==",
+ "dev": true,
+ "requires": {
+ "@sveltejs/vite-plugin-svelte": "^1.0.0-next.9",
+ "cheap-watch": "^1.0.3",
+ "sade": "^1.7.4"
+ }
+ },
+ "@sveltejs/vite-plugin-svelte": {
+ "version": "1.0.0-next.9",
+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.9.tgz",
+ "integrity": "sha512-ySB/GJsZV3h3jqjq5WIiaxVFkJK6vqtG9gS7Iw6SfUH9ZiFNw5JjQF69g68j9cNep3q4yRIYiG5/pI3YIdXEuA==",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^4.1.0",
+ "chalk": "^4.1.1",
+ "debug": "^4.3.2",
+ "hash-sum": "^2.0.0",
+ "require-relative": "^0.8.7",
+ "slash": "^4.0.0",
+ "source-map": "^0.7.3",
+ "svelte-hmr": "^0.14.2"
+ }
+ },
+ "@types/node": {
+ "version": "15.0.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
+ "integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA==",
+ "dev": true
+ },
+ "@types/pug": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.4.tgz",
+ "integrity": "sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI=",
+ "dev": true
+ },
+ "@types/sass": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.16.0.tgz",
+ "integrity": "sha512-2XZovu4NwcqmtZtsBR5XYLw18T8cBCnU2USFHTnYLLHz9fkhnoEMoDsqShJIOFsFhn5aJHjweiUUdTrDGujegA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "axios": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
+ "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
+ "requires": {
+ "follow-redirects": "^1.10.0"
+ }
+ },
+ "chalk": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "cheap-watch": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cheap-watch/-/cheap-watch-1.0.3.tgz",
+ "integrity": "sha512-xC5CruMhLzjPwJ5ecUxGu1uGmwJQykUhqd2QrCrYbwvsFYdRyviu6jG9+pccwDXJR/OpmOTOJ9yLFunVgQu9wg==",
+ "dev": true
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "colorette": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
+ "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
+ "dev": true
+ },
+ "copy-to-clipboard": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz",
+ "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==",
+ "requires": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
+ "debug": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+ "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "detect-indent": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz",
+ "integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==",
+ "dev": true
+ },
+ "esbuild": {
+ "version": "0.9.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.9.7.tgz",
+ "integrity": "sha512-VtUf6aQ89VTmMLKrWHYG50uByMF4JQlVysb8dmg6cOgW8JnFCipmz7p+HNBl+RR3LLCuBxFGVauAe2wfnF9bLg==",
+ "dev": true
+ },
+ "estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "follow-redirects": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.0.tgz",
+ "integrity": "sha512-0vRwd7RKQBTt+mgu87mtYeofLFZpTas2S9zY+jIeuLJMNvudIgF52nr19q40HOwH5RrhWIPuj9puybzSJiRrVg=="
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "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==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "hash-sum": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
+ "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==",
+ "dev": true
+ },
+ "is-core-module": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz",
+ "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "min-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+ "dev": true
+ },
+ "mri": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz",
+ "integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "nanoid": {
+ "version": "3.1.22",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz",
+ "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz",
+ "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==",
+ "dev": true
+ },
+ "postcss": {
+ "version": "8.2.13",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.13.tgz",
+ "integrity": "sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ==",
+ "dev": true,
+ "requires": {
+ "colorette": "^1.2.2",
+ "nanoid": "^3.1.22",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "require-relative": {
+ "version": "0.8.7",
+ "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
+ "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ }
+ },
+ "rollup": {
+ "version": "2.46.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.46.0.tgz",
+ "integrity": "sha512-qPGoUBNl+Z8uNu0z7pD3WPTABWRbcOwIrO/5ccDJzmrtzn0LVf6Lj91+L5CcWhXl6iWf23FQ6m8Jkl2CmN1O7Q==",
+ "dev": true,
+ "requires": {
+ "fsevents": "~2.3.1"
+ }
+ },
+ "sade": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz",
+ "integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==",
+ "dev": true,
+ "requires": {
+ "mri": "^1.1.0"
+ }
+ },
+ "slash": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
+ "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true
+ },
+ "strip-indent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+ "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+ "dev": true,
+ "requires": {
+ "min-indent": "^1.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "svelte": {
+ "version": "3.38.0",
+ "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.38.0.tgz",
+ "integrity": "sha512-V0CbyzvXEka7zQtRYt++cpPh0zxxDUTIeNlq4KncGXn2qHnQFZ2i4L4+2QOfLwOudb9cewJeegupBQcscnBaaA==",
+ "dev": true
+ },
+ "svelte-hmr": {
+ "version": "0.14.3",
+ "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.3.tgz",
+ "integrity": "sha512-N56xX405zLMw2tpGHKRx5h+kmdeZwxI21pvyC6OyBHJDCF6DlwWBm9TifdQmSD4dloWSmpDPzHWYa3CSjfopUg==",
+ "dev": true,
+ "requires": {}
+ },
+ "svelte-preprocess": {
+ "version": "4.7.2",
+ "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.7.2.tgz",
+ "integrity": "sha512-EToG+08rEsA33btv+C5g2qnRArwpTc5AoU0QBB3ZEkYagxAb2yPNsy0qsmtvbJOTBMy6o3oyijDdl3DMpMvpEg==",
+ "dev": true,
+ "requires": {
+ "@types/pug": "^2.0.4",
+ "@types/sass": "^1.16.0",
+ "detect-indent": "^6.0.0",
+ "strip-indent": "^3.0.0"
+ }
+ },
+ "toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
+ },
+ "tslib": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
+ "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
+ "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
+ "dev": true
+ },
+ "vite": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-2.2.3.tgz",
+ "integrity": "sha512-PtjyBL4GtACM+uT5q5hi2+AlMBbb6YI2b2bam6QI8ZdZt4FezseF0yZHQx0G+b3po9jIJ/GS5N9gc5Yq9Rue7g==",
+ "dev": true,
+ "requires": {
+ "esbuild": "^0.9.3",
+ "fsevents": "~2.3.1",
+ "postcss": "^8.2.1",
+ "resolve": "^1.19.0",
+ "rollup": "^2.38.5"
+ }
+ }
+ }
+}
diff --git a/client/package.json b/client/package.json
new file mode 100644
index 0000000..173beea
--- /dev/null
+++ b/client/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "cryptgeon",
+ "private": true,
+ "version": "0.0.1",
+ "scripts": {
+ "dev": "svelte-kit dev",
+ "build": "svelte-kit build",
+ "preview": "svelte-kit preview"
+ },
+ "devDependencies": {
+ "@sveltejs/kit": "next",
+ "svelte": "^3.34.0",
+ "svelte-preprocess": "^4.0.0",
+ "tslib": "^2.0.0",
+ "typescript": "^4.0.0",
+ "vite": "^2.1.0"
+ },
+ "type": "module",
+ "dependencies": {
+ "@fontsource/fira-mono": "^4.2.2",
+ "axios": "^0.21.1",
+ "copy-to-clipboard": "^3.3.1"
+ }
+}
diff --git a/client/src/app.css b/client/src/app.css
new file mode 100644
index 0000000..c72e205
--- /dev/null
+++ b/client/src/app.css
@@ -0,0 +1,62 @@
+@import '@fontsource/fira-mono';
+
+* {
+ box-sizing: border-box;
+}
+
+:root {
+ font-family: 'Fira Mono', monospace;
+
+ --ui-bg-0: #fefefe;
+ --ui-bg-0-85: #ffffffd9;
+ --ui-bg-1: #eee;
+ --ui-bg-2: #e2e2e2;
+ --ui-text-0: #111;
+ --ui-text-1: #222;
+ --ui-clr-primary: hsl(186, 65%, 55%);
+ --ui-clr-error: hsl(357, 77%, 51%);
+
+ --ui-anim: all 150ms ease;
+}
+
+.error-text {
+ color: var(--ui-clr-error);
+}
+
+body {
+ min-height: 100vh;
+ margin: 0;
+ background-color: var(--ui-bg-0);
+ color: var(--ui-text-0);
+}
+
+a {
+ color: inherit;
+ text-decoration: inherit;
+ box-sizing: content-box;
+ border-bottom: 2px solid var(--ui-bg-2);
+ cursor: pointer;
+}
+
+a:active {
+ border-color: var(--ui-clr-primary);
+}
+
+a:hover {
+ border-color: var(--ui-text-0);
+}
+
+input,
+textarea,
+button {
+ appearance: none;
+ transition: var(--ui-anim);
+ font-family: inherit;
+ font-size: inherit;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
diff --git a/client/src/app.html b/client/src/app.html
new file mode 100644
index 0000000..97f318d
--- /dev/null
+++ b/client/src/app.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+ %svelte.head%
+
+
+ %svelte.body%
+
+
diff --git a/client/src/global.d.ts b/client/src/global.d.ts
new file mode 100644
index 0000000..79d7d7f
--- /dev/null
+++ b/client/src/global.d.ts
@@ -0,0 +1,3 @@
+///
+///
+///
diff --git a/client/src/lib/Create.svelte b/client/src/lib/Create.svelte
new file mode 100644
index 0000000..4552ff1
--- /dev/null
+++ b/client/src/lib/Create.svelte
@@ -0,0 +1,151 @@
+
+
+{#if result}
+ {#if result.password}
+
+
+ {/if}
+
+
+ new
+{:else}
+
+{/if}
+
+
diff --git a/client/src/lib/Header/Logo.svg b/client/src/lib/Header/Logo.svg
new file mode 100644
index 0000000..7d21ed8
--- /dev/null
+++ b/client/src/lib/Header/Logo.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3027505a9d404680bbff5824b188538645acde614c2783a6700ff2717fcaa6b8
+size 3897
diff --git a/client/src/lib/Header/index.svelte b/client/src/lib/Header/index.svelte
new file mode 100644
index 0000000..18e1865
--- /dev/null
+++ b/client/src/lib/Header/index.svelte
@@ -0,0 +1,74 @@
+
+
+
+
+
diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts
new file mode 100644
index 0000000..855c2ad
--- /dev/null
+++ b/client/src/lib/api.ts
@@ -0,0 +1,37 @@
+import axios from 'axios'
+
+const base = axios.create({ baseURL: 'http://localhost:5000' })
+
+export type Note = {
+ contents: string
+ password: boolean
+ views?: number
+ expiration?: number
+}
+export type NoteInfo = Pick
+export type NotePublic = Pick
+
+export async function create(note: Note) {
+ const { data } = await base({
+ url: '/notes/',
+ method: 'post',
+ data: note,
+ })
+ return data as { id: string }
+}
+
+export async function get(id: string) {
+ const { data } = await base({
+ url: `/notes/${id}`,
+ method: 'delete',
+ })
+ return data as NotePublic
+}
+
+export async function info(id: string) {
+ const { data } = await base({
+ url: `/notes/${id}`,
+ method: 'get',
+ })
+ return data as NoteInfo
+}
diff --git a/client/src/lib/crypto.ts b/client/src/lib/crypto.ts
new file mode 100644
index 0000000..a5292d8
--- /dev/null
+++ b/client/src/lib/crypto.ts
@@ -0,0 +1,71 @@
+export class Hex {
+ static encode(buffer: ArrayBuffer): string {
+ let s = ''
+ for (const i of new Uint8Array(buffer)) {
+ s += i.toString(16).padStart(2, '0')
+ }
+ return s
+ }
+
+ static decode(s: string): ArrayBuffer {
+ const size = s.length / 2
+ const buffer = new Uint8Array(size)
+ for (let i = 0; i < size; i++) {
+ const idx = i * 2
+ const segment = s.slice(idx, idx + 2)
+ buffer[i] = parseInt(segment, 16)
+ }
+ return buffer
+ }
+}
+
+const ALG = 'AES-GCM'
+
+export function getRandomBytes(size = 16): Uint8Array {
+ return window.crypto.getRandomValues(new Uint8Array(size))
+}
+
+export function getKeyFromString(password: string) {
+ return window.crypto.subtle.importKey(
+ 'raw',
+ new TextEncoder().encode(password),
+ 'PBKDF2',
+ false,
+ ['deriveBits', 'deriveKey']
+ )
+}
+
+export async function getDerivedForKey(key: CryptoKey, salt: ArrayBuffer) {
+ const iterations = 1_000
+ return window.crypto.subtle.deriveKey(
+ {
+ name: 'PBKDF2',
+ salt,
+ iterations,
+ hash: 'SHA-512',
+ },
+ key,
+ { name: ALG, length: 256 },
+ true,
+ ['encrypt', 'decrypt']
+ )
+}
+
+export async function encrypt(plaintext: string, key: CryptoKey) {
+ const salt = getRandomBytes(16)
+ const derived = await getDerivedForKey(key, salt)
+ const iv = getRandomBytes(16)
+ const encrypted = await window.crypto.subtle.encrypt(
+ { name: ALG, iv },
+ derived,
+ new TextEncoder().encode(plaintext)
+ )
+ return [salt, iv, encrypted].map(Hex.encode).join(':')
+}
+
+export async function decrypt(ciphertext: string, key: CryptoKey) {
+ const [salt, iv, encrypted] = ciphertext.split(':').map(Hex.decode)
+ const derived = await getDerivedForKey(key, salt)
+ const plaintext = await window.crypto.subtle.decrypt({ name: ALG, iv }, derived, encrypted)
+ return new TextDecoder().decode(plaintext)
+}
diff --git a/client/src/lib/ui/Button.svelte b/client/src/lib/ui/Button.svelte
new file mode 100644
index 0000000..af9401b
--- /dev/null
+++ b/client/src/lib/ui/Button.svelte
@@ -0,0 +1,18 @@
+
+
+
diff --git a/client/src/lib/ui/Icon.svelte b/client/src/lib/ui/Icon.svelte
new file mode 100644
index 0000000..7af1f80
--- /dev/null
+++ b/client/src/lib/ui/Icon.svelte
@@ -0,0 +1,34 @@
+
+
+{#if html === null}
+
+{:else}
+
+ {@html html}
+
+{/if}
+
+
diff --git a/client/src/lib/ui/Switch.svelte b/client/src/lib/ui/Switch.svelte
new file mode 100644
index 0000000..2253aa9
--- /dev/null
+++ b/client/src/lib/ui/Switch.svelte
@@ -0,0 +1,65 @@
+
+
+
+
+ {label}
+
+
+
+
+
+
diff --git a/client/src/lib/ui/TextArea.svelte b/client/src/lib/ui/TextArea.svelte
new file mode 100644
index 0000000..4ce46ae
--- /dev/null
+++ b/client/src/lib/ui/TextArea.svelte
@@ -0,0 +1,29 @@
+
+
+
+
+ {label}
+
+
+
+
+
diff --git a/client/src/lib/ui/TextInput.svelte b/client/src/lib/ui/TextInput.svelte
new file mode 100644
index 0000000..c2d3986
--- /dev/null
+++ b/client/src/lib/ui/TextInput.svelte
@@ -0,0 +1,91 @@
+
+
+
+
+ {label}
+
+
+
+ {#if isPassword}
+
+ {/if}
+ {#if random}
+
+ {/if}
+ {#if copy}
+
+ {/if}
+
+
+
+
diff --git a/client/src/routes/$layout.svelte b/client/src/routes/$layout.svelte
new file mode 100644
index 0000000..94b2ab6
--- /dev/null
+++ b/client/src/routes/$layout.svelte
@@ -0,0 +1,45 @@
+
+
+
+ cryptgeon
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/routes/about.svelte b/client/src/routes/about.svelte
new file mode 100644
index 0000000..e0342c2
--- /dev/null
+++ b/client/src/routes/about.svelte
@@ -0,0 +1,55 @@
+
+
+
+ About
+
+
+
+ About
+
+
+ cryptgeon is an secure, open source sharing note service inspired by
+ PrivNote .
+
+
+
+ ▶ how does it work?
+
+ each note has a 512bit generated id that is used to retrieve the note. data is stored in memory
+ and never persisted to disk.
+
+
+ ▶ Features
+
+ view and time constrains
+ in memory, no persistence
+ in browser encryption → server cannot decrypt contents
+
+
+
+ ▶ tech stack
+
+ the backend is written in rust and the frontend is svelte and typescript.
+
+ you are welcomed to check & audit the
+ source code .
+
+
+
+
diff --git a/client/src/routes/index.svelte b/client/src/routes/index.svelte
new file mode 100644
index 0000000..3d3df3c
--- /dev/null
+++ b/client/src/routes/index.svelte
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/client/src/routes/note/[id].svelte b/client/src/routes/note/[id].svelte
new file mode 100644
index 0000000..02d4b82
--- /dev/null
+++ b/client/src/routes/note/[id].svelte
@@ -0,0 +1,94 @@
+
+
+
+
+{#if !loading}
+ {#if !exists}
+ note was not found or was already deleted.
+ {:else if note && !error}
+ you will not get the chance to see the note again.
+
+ {note.contents}
+
+
+ copy(note.contents)}>copy to clipboard
+ {:else}
+
+ click below to show and delete the note if the counter has reached it's limit
+ {#if needPassword}
+
+
+ {/if}
+ show note
+ {#if error}
+
+ wrong password. could not decipher.
+ {/if}
+
+ {/if}
+{/if}
+
+
diff --git a/client/static/favicon.ico b/client/static/favicon.ico
new file mode 100644
index 0000000..d75d248
Binary files /dev/null and b/client/static/favicon.ico differ
diff --git a/client/static/favicon.png b/client/static/favicon.png
new file mode 100644
index 0000000..c101564
Binary files /dev/null and b/client/static/favicon.png differ
diff --git a/client/static/icons/copy-sharp.svg b/client/static/icons/copy-sharp.svg
new file mode 100644
index 0000000..1ba7f2a
--- /dev/null
+++ b/client/static/icons/copy-sharp.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6cc1faf995549ec8034a5c6eaff7c0c09c16ca5f7493e94126b0d63d97b76fdd
+size 313
diff --git a/client/static/icons/dice-sharp.svg b/client/static/icons/dice-sharp.svg
new file mode 100644
index 0000000..52636ce
--- /dev/null
+++ b/client/static/icons/dice-sharp.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:02c57a40713e3824ad8d85cdcb9608476f601319224fccbec712e270c1b14a99
+size 728
diff --git a/client/static/icons/eye-off-sharp.svg b/client/static/icons/eye-off-sharp.svg
new file mode 100644
index 0000000..b1e2d72
--- /dev/null
+++ b/client/static/icons/eye-off-sharp.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1f23e318db6d604bde434608118f4538e7e552229b2111ee106614a2e2783221
+size 720
diff --git a/client/static/icons/eye-sharp.svg b/client/static/icons/eye-sharp.svg
new file mode 100644
index 0000000..d260a0c
--- /dev/null
+++ b/client/static/icons/eye-sharp.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:10ff9341baee15c8fee6b523dad65328b81d07ca4fa0bd08095ec16506f6ba04
+size 474
diff --git a/client/static/icons/lock-closed-sharp.svg b/client/static/icons/lock-closed-sharp.svg
new file mode 100644
index 0000000..27395d2
--- /dev/null
+++ b/client/static/icons/lock-closed-sharp.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:549d81fe7202d7fb1265d7934366ef96c821f904164b4c0456444f2a1f000fe1
+size 275
diff --git a/client/static/robots.txt b/client/static/robots.txt
new file mode 100644
index 0000000..e9e57dc
--- /dev/null
+++ b/client/static/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/client/svelte.config.cjs b/client/svelte.config.cjs
new file mode 100644
index 0000000..9133017
--- /dev/null
+++ b/client/svelte.config.cjs
@@ -0,0 +1,13 @@
+const preprocess = require('svelte-preprocess');
+
+/** @type {import('@sveltejs/kit').Config} */
+module.exports = {
+ // Consult https://github.com/sveltejs/svelte-preprocess
+ // for more information about preprocessors
+ preprocess: preprocess(),
+
+ kit: {
+ // hydrate the element in src/app.html
+ target: '#svelte'
+ }
+};
diff --git a/client/tsconfig.json b/client/tsconfig.json
new file mode 100644
index 0000000..d826f6e
--- /dev/null
+++ b/client/tsconfig.json
@@ -0,0 +1,30 @@
+{
+ "compilerOptions": {
+ "moduleResolution": "node",
+ "module": "es2020",
+ "lib": ["es2020"],
+ "target": "es2019",
+ /**
+ svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
+ to enforce using \`import type\` instead of \`import\` for Types.
+ */
+ "importsNotUsedAsValues": "error",
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ /**
+ To have warnings/errors of the Svelte compiler at the correct position,
+ enable source maps by default.
+ */
+ "sourceMap": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "baseUrl": ".",
+ "allowJs": true,
+ "checkJs": true,
+ "paths": {
+ "$lib/*": ["src/lib/*"]
+ }
+ },
+ "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"]
+}