save local sessions dialog

This commit is contained in:
Rachel Powers
2024-04-20 20:40:29 -07:00
parent 599ad38adc
commit 185449404d
11 changed files with 653 additions and 80 deletions

View File

@@ -39,6 +39,7 @@
"mini-css-extract-plugin": "^2.9.0",
"postcss-loader": "^8.1.1",
"sass": "^1.75.0",
"tailwindcss": "^3.4.3",
"ts-lit-plugin": "^2.0.2",
"ts-loader": "^9.5.1",
"typescript": "^5.4.5",

347
www/pnpm-lock.yaml generated
View File

@@ -109,6 +109,9 @@ devDependencies:
sass:
specifier: ^1.75.0
version: 1.75.0
tailwindcss:
specifier: ^3.4.3
version: 3.4.3
ts-lit-plugin:
specifier: ^2.0.2
version: 2.0.2
@@ -124,6 +127,11 @@ devDependencies:
packages:
/@alloc/quick-lru@5.2.0:
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
dev: true
/@babel/code-frame@7.24.2:
resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==}
engines: {node: '>=6.9.0'}
@@ -201,6 +209,18 @@ packages:
resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
dev: false
/@isaacs/cliui@8.0.2:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
dependencies:
string-width: 5.1.2
string-width-cjs: /string-width@4.2.3
strip-ansi: 7.1.0
strip-ansi-cjs: /strip-ansi@6.0.1
wrap-ansi: 8.1.0
wrap-ansi-cjs: /wrap-ansi@7.0.0
dev: true
/@jridgewell/gen-mapping@0.3.5:
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
@@ -458,6 +478,13 @@ packages:
'@types/emscripten': 1.39.10
dev: true
/@pkgjs/parseargs@0.11.0:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
requiresBuild: true
dev: true
optional: true
/@polka/url@1.0.0-next.25:
resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==}
dev: true
@@ -1215,6 +1242,11 @@ packages:
engines: {node: '>=8'}
dev: true
/ansi-regex@6.0.1:
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
engines: {node: '>=12'}
dev: true
/ansi-styles@3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
@@ -1228,6 +1260,15 @@ packages:
dependencies:
color-convert: 2.0.1
/ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
dev: true
/any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
dev: true
/anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
@@ -1236,6 +1277,10 @@ packages:
picomatch: 2.3.1
dev: true
/arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
dev: true
/argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
dev: true
@@ -1345,6 +1390,12 @@ packages:
balanced-match: 1.0.2
concat-map: 0.0.1
/brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
dependencies:
balanced-match: 1.0.2
dev: true
/braces@3.0.2:
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
engines: {node: '>=8'}
@@ -1464,6 +1515,11 @@ packages:
engines: {node: '>=6'}
dev: true
/camelcase-css@2.0.1:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'}
dev: true
/caniuse-lite@1.0.30001610:
resolution: {integrity: sha512-QFutAY4NgaelojVMjY63o6XlZyORPaLfyMnsl3HgnWdJUcX6K0oaJymHjH8PT5Gk7sTm8rvC/c5COUQKXqmOMA==}
dev: true
@@ -1579,6 +1635,11 @@ packages:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
dev: true
/commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
dev: true
/commander@7.2.0:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
engines: {node: '>= 10'}
@@ -1773,6 +1834,12 @@ packages:
engines: {node: '>= 6'}
dev: true
/cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
hasBin: true
dev: true
/csso@5.0.5:
resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
@@ -1868,6 +1935,10 @@ packages:
lodash.deburr: 4.1.0
dev: true
/didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
dev: true
/diffie-hellman@5.0.3:
resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
dependencies:
@@ -1876,6 +1947,10 @@ packages:
randombytes: 2.1.0
dev: false
/dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
dev: true
/dns-packet@5.6.1:
resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==}
engines: {node: '>=6'}
@@ -1914,6 +1989,10 @@ packages:
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
dev: true
/eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true
/ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
dev: true
@@ -1938,6 +2017,10 @@ packages:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: true
/emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
dev: true
/encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
@@ -2213,6 +2296,14 @@ packages:
optional: true
dev: true
/foreground-child@3.1.1:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'}
dependencies:
cross-spawn: 7.0.3
signal-exit: 4.1.0
dev: true
/fork-ts-checker-webpack-plugin@9.0.2(typescript@5.4.5)(webpack@5.91.0):
resolution: {integrity: sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==}
engines: {node: '>=12.13.0', yarn: '>=1.0.0'}
@@ -2302,10 +2393,29 @@ packages:
is-glob: 4.0.3
dev: true
/glob-parent@6.0.2:
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
engines: {node: '>=10.13.0'}
dependencies:
is-glob: 4.0.3
dev: true
/glob-to-regexp@0.4.1:
resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
dev: true
/glob@10.3.12:
resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
dependencies:
foreground-child: 3.1.1
jackspeak: 2.3.6
minimatch: 9.0.4
minipass: 7.0.4
path-scurry: 1.10.2
dev: true
/glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
dependencies:
@@ -2634,6 +2744,15 @@ packages:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
/jackspeak@2.3.6:
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
engines: {node: '>=14'}
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
dev: true
/jest-worker@27.5.1:
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
engines: {node: '>= 10.13.0'}
@@ -2708,6 +2827,16 @@ packages:
engines: {node: '>=6'}
dev: true
/lilconfig@2.1.0:
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
engines: {node: '>=10'}
dev: true
/lilconfig@3.1.1:
resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==}
engines: {node: '>=14'}
dev: true
/lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
dev: true
@@ -2770,6 +2899,11 @@ packages:
/lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
/lru-cache@10.2.0:
resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
engines: {node: 14 || >=16.14}
dev: true
/lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
@@ -2894,6 +3028,18 @@ packages:
dependencies:
brace-expansion: 1.1.11
/minimatch@9.0.4:
resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
brace-expansion: 2.0.1
dev: true
/minipass@7.0.4:
resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
engines: {node: '>=16 || 14 >=14.17'}
dev: true
/mrmime@1.0.1:
resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==}
engines: {node: '>=10'}
@@ -2919,6 +3065,14 @@ packages:
thunky: 1.1.0
dev: true
/mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
dependencies:
any-promise: 1.3.0
object-assign: 4.1.1
thenify-all: 1.6.0
dev: true
/nanoid@3.3.7:
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -2977,6 +3131,16 @@ packages:
boolbase: 1.0.0
dev: true
/object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
dev: true
/object-hash@3.0.0:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
dev: true
/object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
dev: true
@@ -3082,6 +3246,14 @@ packages:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
/path-scurry@1.10.2:
resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
lru-cache: 10.2.0
minipass: 7.0.4
dev: true
/path-to-regexp@0.1.7:
resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
dev: true
@@ -3111,6 +3283,55 @@ packages:
engines: {node: '>=8.6'}
dev: true
/pify@2.3.0:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
dev: true
/pirates@4.0.6:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
dev: true
/postcss-import@15.1.0(postcss@8.4.38):
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
engines: {node: '>=14.0.0'}
peerDependencies:
postcss: ^8.0.0
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.8
dev: true
/postcss-js@4.0.1(postcss@8.4.38):
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
engines: {node: ^12 || ^14 || >= 16}
peerDependencies:
postcss: ^8.4.21
dependencies:
camelcase-css: 2.0.1
postcss: 8.4.38
dev: true
/postcss-load-config@4.0.2(postcss@8.4.38):
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
engines: {node: '>= 14'}
peerDependencies:
postcss: '>=8.0.9'
ts-node: '>=9.0.0'
peerDependenciesMeta:
postcss:
optional: true
ts-node:
optional: true
dependencies:
lilconfig: 3.1.1
postcss: 8.4.38
yaml: 2.4.1
dev: true
/postcss-loader@8.1.1(@rspack/core@0.6.2)(postcss@8.4.38)(typescript@5.4.5)(webpack@5.91.0):
resolution: {integrity: sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==}
engines: {node: '>= 18.12.0'}
@@ -3134,6 +3355,28 @@ packages:
- typescript
dev: true
/postcss-nested@6.0.1(postcss@8.4.38):
resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
engines: {node: '>=12.0'}
peerDependencies:
postcss: ^8.2.14
dependencies:
postcss: 8.4.38
postcss-selector-parser: 6.0.16
dev: true
/postcss-selector-parser@6.0.16:
resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==}
engines: {node: '>=4'}
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
dev: true
/postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
dev: true
/postcss@8.4.38:
resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
@@ -3229,6 +3472,12 @@ packages:
unpipe: 1.0.0
dev: true
/read-cache@1.0.0:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
dependencies:
pify: 2.3.0
dev: true
/readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
dependencies:
@@ -3511,6 +3760,11 @@ packages:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
dev: true
/signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
dev: true
/sirv@1.0.19:
resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==}
engines: {node: '>= 10'}
@@ -3602,6 +3856,15 @@ packages:
strip-ansi: 6.0.1
dev: true
/string-width@5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
dependencies:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.1.0
dev: true
/string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
dependencies:
@@ -3619,6 +3882,13 @@ packages:
ansi-regex: 5.0.1
dev: true
/strip-ansi@7.1.0:
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
engines: {node: '>=12'}
dependencies:
ansi-regex: 6.0.1
dev: true
/strip-final-newline@2.0.0:
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
engines: {node: '>=6'}
@@ -3629,6 +3899,20 @@ packages:
engines: {node: '>=8'}
dev: false
/sucrase@3.35.0:
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
dependencies:
'@jridgewell/gen-mapping': 0.3.5
commander: 4.1.1
glob: 10.3.12
lines-and-columns: 1.2.4
mz: 2.7.0
pirates: 4.0.6
ts-interface-checker: 0.1.13
dev: true
/supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
@@ -3668,6 +3952,37 @@ packages:
picocolors: 1.0.0
dev: true
/tailwindcss@3.4.3:
resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==}
engines: {node: '>=14.0.0'}
hasBin: true
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
chokidar: 3.6.0
didyoumean: 1.2.2
dlv: 1.1.3
fast-glob: 3.3.2
glob-parent: 6.0.2
is-glob: 4.0.3
jiti: 1.21.0
lilconfig: 2.1.0
micromatch: 4.0.5
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.0.0
postcss: 8.4.38
postcss-import: 15.1.0(postcss@8.4.38)
postcss-js: 4.0.1(postcss@8.4.38)
postcss-load-config: 4.0.2(postcss@8.4.38)
postcss-nested: 6.0.1(postcss@8.4.38)
postcss-selector-parser: 6.0.16
resolve: 1.22.8
sucrase: 3.35.0
transitivePeerDependencies:
- ts-node
dev: true
/tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
@@ -3708,6 +4023,19 @@ packages:
source-map-support: 0.5.21
dev: true
/thenify-all@1.6.0:
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
engines: {node: '>=0.8'}
dependencies:
thenify: 3.3.1
dev: true
/thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
dependencies:
any-promise: 1.3.0
dev: true
/thunky@1.1.0:
resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==}
dev: true
@@ -3733,6 +4061,10 @@ packages:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
dev: false
/ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
dev: true
/ts-lit-plugin@2.0.2:
resolution: {integrity: sha512-DPXlVxhjWHxg8AyBLcfSYt2JXgpANV1ssxxwjY98o26gD8MzeiM68HFW9c2VeDd1CjoR3w7B/6/uKxwBQe+ioA==}
dependencies:
@@ -4197,6 +4529,15 @@ packages:
strip-ansi: 6.0.1
dev: true
/wrap-ansi@8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
dependencies:
ansi-styles: 6.2.1
string-width: 5.1.2
strip-ansi: 7.1.0
dev: true
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
@@ -4252,6 +4593,12 @@ packages:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: true
/yaml@2.4.1:
resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==}
engines: {node: '>= 14'}
hasBin: true
dev: true
/yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}

5
www/postcss.config.cjs Normal file
View File

@@ -0,0 +1,5 @@
module.exports = {
plugins: {
tailwindcss: {},
},
};

View File

@@ -1,8 +1,9 @@
import { defineConfig } from "@rsbuild/core";
import { pluginTypeCheck } from "@rsbuild/plugin-type-check";
import { pluginImageCompress } from '@rsbuild/plugin-image-compress';
import { pluginImageCompress } from "@rsbuild/plugin-image-compress";
const rspack = require("@rspack/core");
const { CssExtractRspackPlugin } = require("@rspack/core");
const path = require("path");
const commitHash = require("child_process")
@@ -38,6 +39,7 @@ export default defineConfig({
},
],
}),
new CssExtractRspackPlugin(),
new rspack.DefinePlugin({
__COMMIT_HASH__: JSON.stringify(commitHash),
__BUILD_DATE__: JSON.stringify(new Date()),
@@ -63,8 +65,5 @@ export default defineConfig({
template: "./src/index.html",
},
},
plugins: [
pluginTypeCheck(),
pluginImageCompress(),
],
plugins: [pluginTypeCheck(), pluginImageCompress()],
});

View File

@@ -72,4 +72,8 @@ $accordion-button-padding-y: 0.5rem;
//
// Custom styles
//
@import "./dark.scss"
@import "./dark.scss";
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -82,6 +82,7 @@ export class App extends BaseElement {
root.addEventListener("app-open-file", this._handleOpenFile.bind(this));
root.addEventListener("app-export", this._handleExport.bind(this));
root.addEventListener("app-save", this._handleSave.bind(this));
root.addEventListener("app-load", this._handleLoad.bind(this));
return root;
}
@@ -122,6 +123,10 @@ export class App extends BaseElement {
_handleSave(_e: Event) {
this.saveDialog.show("save");
}
_handleLoad(_e: Event) {
this.saveDialog.show("load");
}
_handleOpenFile(_e: Event) {
openFile(window.Editor.editor);
}

View File

@@ -7,20 +7,54 @@ import "@shoelace-style/shoelace/dist/components/dialog/dialog.js";
import "@shoelace-style/shoelace/dist/components/format-date/format-date.js";
import "@shoelace-style/shoelace/dist/components/relative-time/relative-time.js";
import "@shoelace-style/shoelace/dist/components/format-bytes/format-bytes.js";
import "@shoelace-style/shoelace/dist/components/spinner/spinner.js";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
import { repeat } from "lit/directives/repeat.js";
import SlDialog from "@shoelace-style/shoelace/dist/components/dialog/dialog.js";
import { when } from "lit/directives/when.js";
import uFuzzy from "@leeoniya/ufuzzy";
import SlButton from "@shoelace-style/shoelace/dist/components/button/button.js";
import { SlIconButton } from "@shoelace-style/shoelace";
export type SaveDialogMode = "save" | "load";
@customElement("save-dialog")
export class SaveDialog extends BaseElement {
static styles = [...defaultCss];
static styles = [
...defaultCss,
css`
.save-dialog {
--width: 42rem;
}
sl-icon-button.delete-button::part(base) {
color: var(--sl-color-danger-600);
}
sl-icon-button.delete-button::part(base):hover,
sl-icon-button.delete-button::part(base):focus {
color: var(--sl-color-danger-500);
}
sl-icon-button.delete-button::part(base):active {
color: var(--sl-color-danger-600);
}
`,
];
private _saves: { name: string; date: Date; session: VMState }[];
get saves() {
return this._saves;
}
@state()
set saves(val: { name: string; date: Date; session: VMState }[]) {
this._saves = val;
this.performSearch();
}
@state() saves: { name: string; date: Date; session: VMState }[];
@state() mode: SaveDialogMode;
private searchResults: { name: string; date: Date; session: VMState }[];
constructor() {
super();
this.mode = "save";
@@ -48,83 +82,247 @@ export class SaveDialog extends BaseElement {
});
}
@query("sl-dialog") dialog: SlDialog;
@query("sl-dialog.save-dialog") saveDialog: SlDialog;
show(mode: SaveDialogMode) {
this.mode = mode;
this.dialog.show();
this.saveDialog.show();
}
hide() {
this.dialog.hide();
this.saveDialog.hide();
}
private _filter: string = "";
get filter() {
return this._filter;
}
@state()
set filter(val: string) {
this._filter = val;
this.performSearch();
}
performSearch() {
if (this.filter) {
const source = this.saves ?? [];
const haystack: string[] = source.map((save) => save.name);
const uf = new uFuzzy({});
const [_idxs, info, order] = uf.search(haystack, this._filter, 0, 1e3);
const filtered = order?.map((infoIdx) => source[info.idx[infoIdx]]);
this.searchResults = filtered ?? [];
} else {
this.searchResults = this.saves;
}
}
render() {
const label = this.mode === "save" ? "Save Session" : "Load session";
return html`
<sl-dialog label="Save Session">
<sl-dialog label=${label} class="save-dialog" @sl-hide=${this._saveDialogHide}>
${when(
this.mode === "save",
() => html`
<div class="hstack mb-2">
<sl-input class="save-name-input" autofocus></sl-input>
<div class="flex flex-row mb-4">
<sl-input
class="save-name-input grow"
autofocus
pill
@sl-input=${this._saveInputChange}
></sl-input>
<sl-button
class="ms-2"
class="ms-2 save-button"
variant="success"
@click=${this._handleSaveClick}
pill
@click=${this._handleSaveButtonClick}
>Save</sl-button
>
</div>
`,
)}
<sl-input
class="filter-input"
?autofocus=${this.mode === "load"}
placeholder="Filter Saves"
clearable
@sl-input=${this._handleSearchInput}
>
<sl-icon slot="suffix" name="search"></sl-icon>
</sl-input>
<table>
<tr>
<th>Name</th>
<th>Date</th>
<th>Size</th>
</tr>
${when(typeof this.saves !== "undefined", () =>
repeat(
this.saves,
(save) => save.name,
(save) => {
const size = JSON.stringify(save.session).length;
return html`
<tr>
<td>${save.name}</td>
<td>
<sl-format-date .date=${save.date}></sl-format-date>
<sl-relative-time .date=${save.date}></sl-relative-time>
</td>
<td>
<sl-format-bytes .value=${size}></sl-format-bytes>
</td>
</tr>
`;
},
),
)}
</table>
${when(
typeof this.saves !== "undefined",
() => html`
${when(
this.saves.length === 0,
() => html`<strong>No local saves found</strong>`,
() => html`
<div
class="p-2 border-1 border-solid border-neutral-700 rounded-lg"
>
<sl-input
class="filter-input mb-2"
?autofocus=${this.mode === "load"}
placeholder="Filter Saves"
clearable
size="small"
@sl-input=${this._handleSearchInput}
>
<sl-icon slot="suffix" name="search"></sl-icon>
</sl-input>
<div class="overflow-auto max-h-40">
${repeat(
this.searchResults,
(save) => save.name,
(save, index) => {
const size = JSON.stringify(save.session).length;
let classList =
index !== 0
? "flex flex-row space-x-2 justify-between justify-self-start border-t border-neutral-400/10"
: "flex flex-row space-x-2 justify-between justify-self-start";
classList += " hover:bg-neutral-700 cursor-pointer";
return html`
<div
class=${classList}
value=${save.name}
@click=${this._handleLocalSaveClick}
>
<div class="py-2 ms-2 text-nowrap">
${save.name}
</div>
<div class="py-2 ms-auto text-nowrap">
<sl-relative-time
.date=${save.date}
></sl-relative-time>
<sl-format-date
.date=${save.date}
></sl-format-date>
</div>
<div class="py-2 me-2 text-nowrap">
<sl-format-bytes .value=${size}></sl-format-bytes>
</div>
<div class="py-2 mx-3">
<sl-tooltip content="Delete this save">
<sl-icon-button
name="trash"
label="Delete"
key=${save.name}
@click=${this._handleDeleteSave}
class="delete-button"
></sl-icon-button>
</sl-tooltip>
</div>
</div>
`;
},
)}
</div>
</div>
`,
)}
`,
() => html` <sl-spinner></sl-spinner> `,
)}
</sl-dialog>
<sl-dialog class="delete-dialog" no-header @sl-request-close=${this._preventOverlayClose} @sl-hide=${this._deleteDialogHide}>
<div class="w-full">
<p><strong>Are you sure you want to remove this save?</strong></p>
</div>
<div slot="footer">
<sl-button variant="primary" autofocus @click=${this._closeDeleteDialog}>Close</sl-button>
<sl-button variant="danger" @click=${this._deleteDialogDelete}>Delete</sl-button>
</div>
</sl-dialog>
`;
}
@query(".save-name-input") saveInput: SlInput;
@query("sl-dialog.delete-dialog") deleteDialog: SlDialog;
private _toDelete: string | undefined;
async _handleSaveClick(_e: CustomEvent) {
_preventOverlayClose(event: CustomEvent) {
if (event.detail.source === "overlay") {
event.preventDefault();
}
this._toDelete = undefined;
}
_closeDeleteDialog() {
this.deleteDialog.hide();
}
_deleteDialogHide(e: CustomEvent) {
if (e.target !== e.currentTarget) return;
this._toDelete = undefined;
}
_saveDialogHide(e: CustomEvent) {
if (e.target !== e.currentTarget) return;
if (this.filterInput != null) this.filterInput.value = "";
if (this.saveInput != null) this.saveInput.value = "";
this._filter = undefined;
}
@query(".save-name-input") saveInput: SlInput;
@query(".filter-input") filterInput: SlInput;
@query(".save-button") saveButton: SlButton;
async _handleSaveButtonClick(_e: CustomEvent) {
const name = this.saveInput.value;
const app = await window.App.get();
console.log(app);
await app.session.saveLocal(name);
this.saveDialog.hide();
}
_handleSearchInput() {}
_handleLocalSaveClick(e: Event) {
const saveName = (e.currentTarget as HTMLDivElement).getAttribute("value");
if (this.mode === "save") {
this.saveInput.value = saveName;
this.checkSaveName();
} else {
window.App.get().then((app) => app.session.loadFromLocal(saveName));
this.saveDialog.hide();
}
}
checkSaveName() {
const saveName = this.saveInput.value;
const saves = this.saves ?? [];
let found = false;
for (const save of saves) {
if (save.name === saveName) {
found = true;
break;
}
}
if (found) {
this.saveButton.variant = "danger";
this.saveButton.textContent = "Overwrite";
} else {
this.saveButton.variant = "success";
this.saveButton.textContent = "Save";
}
}
_saveInputChange(e: CustomEvent) {
this.checkSaveName();
}
private _searchTimeout: number | undefined;
_handleSearchInput() {
if (this._searchTimeout) clearTimeout(this._searchTimeout);
const that = this;
this._searchTimeout = setTimeout(() => {
that.filter = that.filterInput.value;
that._searchTimeout = undefined;
}, 200);
}
_handleDeleteSave(e: Event) {
const saveName = (e.target as SlIconButton).getAttribute("key");
this._toDelete = saveName;
this.deleteDialog.show();
}
_deleteDialogDelete() {
if (typeof this._toDelete === "string") {
const toDelete = this._toDelete;
window.App.get().then(app => app.session.deleteLocalSave(toDelete))
}
this.deleteDialog.hide();
this._toDelete = undefined;
this.saveDialog.show();
}
}

View File

@@ -1,36 +1,25 @@
import { CSSResultGroup, LitElement, css, unsafeCSS } from "lit";
import { CSSResultGroup, LitElement, css } from "lit";
import shoelaceDark from "@shoelace-style/shoelace/dist/themes/dark.styles.js";
const { cssRules } = Array.from(document.styleSheets).find(
(sheet) => sheet.href && sheet.href.endsWith("index.css"),
);
const globalStyle = css([
Object.values(cssRules)
.map((rule) => rule.cssText)
.join("\n"),
] as any);
export const defaultCss = [
globalStyle,
shoelaceDark,
css`
.ps-2 {
padding-left: 0.5rem !important;
}
.ms-2 {
margin-left: 0.5rem !important;
}
.ms-auto {
margin-left: auto !important;
}
.flex-row {
flex-direction: row !important;
}
.d-flex {
display: flex !important;
}
.align-self-center {
align-self: center !important;
}
.mb-auto {
margin-bottom: auto !important;
}
.mt-auto {
margin-top: auto !important;
}
.me-2 {
margin-right: 2rem !important;
}
.hstack {
display: flex;
flex-direction: row;

View File

@@ -273,7 +273,7 @@ export class Session extends EventTarget {
const db = await this.openIndexDB();
const transaction = db.transaction(['sessions'], "readwrite");
const sessionStore = transaction.objectStore("sessions");
sessionStore.put({
await sessionStore.put({
name,
date: new Date(),
session: state,
@@ -281,6 +281,22 @@ export class Session extends EventTarget {
this.dispatchEvent(new CustomEvent("sessions-local-update"))
}
async loadFromLocal(name: string) {
const db = await this.openIndexDB();
const save = await db.get("sessions", name);
if (typeof save !== "undefined") {
const { session } = save;
this.load(session);
}
}
async deleteLocalSave(name: string) {
const db = await this.openIndexDB();
const transaction = db.transaction(['sessions'], "readwrite");
const sessionStore = transaction.objectStore("sessions");
await sessionStore.delete(name);
this.dispatchEvent(new CustomEvent("sessions-local-update"))
}
async getLocalSaved() {
const db = await this.openIndexDB();
const sessions = await db.getAll('sessions');

View File

@@ -146,7 +146,8 @@ export async function saveFile(content: BlobPart) {
} else {
console.log("saving file via hidden link event");
var a = document.createElement("a");
a.download = "code.ic10";
const date = new Date().valueOf().toString(16) ;
a.download = `code_${date}.ic10`;
a.href = window.URL.createObjectURL(blob);
a.click();
}

8
www/tailwind.config.js Normal file
View File

@@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{html,js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [],
};