From 185449404d97c275fc9355774ce500df71111e95 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 20 Apr 2024 20:40:29 -0700 Subject: [PATCH] save local sessions dialog --- www/package.json | 1 + www/pnpm-lock.yaml | 347 ++++++++++++++++++++++++++++++++++ www/postcss.config.cjs | 5 + www/rsbuild.config.ts | 9 +- www/src/scss/styles.scss | 6 +- www/src/ts/app/app.ts | 5 + www/src/ts/app/save.ts | 298 ++++++++++++++++++++++++----- www/src/ts/components/base.ts | 33 ++-- www/src/ts/session.ts | 18 +- www/src/ts/utils.ts | 3 +- www/tailwind.config.js | 8 + 11 files changed, 653 insertions(+), 80 deletions(-) create mode 100644 www/postcss.config.cjs create mode 100644 www/tailwind.config.js diff --git a/www/package.json b/www/package.json index 92f3536..465ebd8 100644 --- a/www/package.json +++ b/www/package.json @@ -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", diff --git a/www/pnpm-lock.yaml b/www/pnpm-lock.yaml index bb0fc9a..9f5fff5 100644 --- a/www/pnpm-lock.yaml +++ b/www/pnpm-lock.yaml @@ -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'} diff --git a/www/postcss.config.cjs b/www/postcss.config.cjs new file mode 100644 index 0000000..ee5f90b --- /dev/null +++ b/www/postcss.config.cjs @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + tailwindcss: {}, + }, +}; diff --git a/www/rsbuild.config.ts b/www/rsbuild.config.ts index 18b0bca..1ba6afb 100644 --- a/www/rsbuild.config.ts +++ b/www/rsbuild.config.ts @@ -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()], }); diff --git a/www/src/scss/styles.scss b/www/src/scss/styles.scss index 6c127a2..b78d870 100644 --- a/www/src/scss/styles.scss +++ b/www/src/scss/styles.scss @@ -72,4 +72,8 @@ $accordion-button-padding-y: 0.5rem; // // Custom styles // -@import "./dark.scss" +@import "./dark.scss"; + +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/www/src/ts/app/app.ts b/www/src/ts/app/app.ts index ebb3ce1..9af4435 100644 --- a/www/src/ts/app/app.ts +++ b/www/src/ts/app/app.ts @@ -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); } diff --git a/www/src/ts/app/save.ts b/www/src/ts/app/save.ts index 79642f7..fd3a2fd 100644 --- a/www/src/ts/app/save.ts +++ b/www/src/ts/app/save.ts @@ -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` - + ${when( this.mode === "save", () => html` -
- +
+ Save
`, )} - - - - - - - - - - ${when(typeof this.saves !== "undefined", () => - repeat( - this.saves, - (save) => save.name, - (save) => { - const size = JSON.stringify(save.session).length; - return html` - - - - - - `; - }, - ), - )} -
NameDateSize
${save.name} - - - - -
+ ${when( + typeof this.saves !== "undefined", + () => html` + ${when( + this.saves.length === 0, + () => html`No local saves found`, + () => html` +
+ + + +
+ ${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` +
+
+ ${save.name} +
+
+ + +
+
+ +
+
+ + + +
+
+ `; + }, + )} +
+
+ `, + )} + `, + () => html` `, + )} + + +
+

Are you sure you want to remove this save?

+
+
+ Close + Delete +
`; } - @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(); + } } diff --git a/www/src/ts/components/base.ts b/www/src/ts/components/base.ts index 65f410e..eadd2ad 100644 --- a/www/src/ts/components/base.ts +++ b/www/src/ts/components/base.ts @@ -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; diff --git a/www/src/ts/session.ts b/www/src/ts/session.ts index 78780f9..6125097 100644 --- a/www/src/ts/session.ts +++ b/www/src/ts/session.ts @@ -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'); diff --git a/www/src/ts/utils.ts b/www/src/ts/utils.ts index 44b0db6..e7e7b07 100644 --- a/www/src/ts/utils.ts +++ b/www/src/ts/utils.ts @@ -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(); } diff --git a/www/tailwind.config.js b/www/tailwind.config.js new file mode 100644 index 0000000..7197c6a --- /dev/null +++ b/www/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.{html,js,ts,jsx,tsx}'], + theme: { + extend: {}, + }, + plugins: [], +};