From ea68fa5cc4a8009df82bf554495470a0527002dc Mon Sep 17 00:00:00 2001 From: thanawat saiyota Date: Tue, 9 Jun 2026 10:50:59 +0700 Subject: [PATCH] create branch dev and commit code --- bun.lock | 1097 +++++++ bun.lockb | Bin 197391 -> 0 bytes package.json | 2 +- src/app.d.ts | 5 + .../android-recipe-export-view.svelte | 618 ++++ src/lib/components/app-sidebar.svelte | 90 +- .../recipe-details/recipe-detail.svelte | 9 +- .../components/recipe-editor-dialog.svelte | 16 + src/lib/core/adb/adb.ts | 420 ++- src/lib/core/auth/domainBlocker.ts | 1 - src/lib/core/brew/command.ts | 20 +- src/lib/core/handlers/adbPayloadHandler.ts | 92 +- src/lib/core/handlers/messageHandler.ts | 139 +- src/lib/core/handlers/ws_messageSender.ts | 4 +- .../services/androidRecipeExportService.ts | 284 ++ src/lib/core/services/sheetService.ts | 251 ++ src/lib/core/stores/adbWriter.ts | 21 +- src/lib/core/stores/genLayoutStore.ts | 249 ++ src/lib/core/stores/menuSaveStore.ts | 85 + src/lib/core/stores/sheetStore.ts | 805 +++++ src/lib/core/stores/websocketStore.ts | 40 + src/lib/core/types/catalogData.ts | 12 + src/lib/core/types/outMessage.ts | 9 + src/lib/core/utils/productCode.ts | 162 + src/lib/data/productcode.txt | 1 + src/lib/workers/androidRecipeExport.worker.ts | 121 + src/routes/(authed)/+layout.svelte | 69 +- src/routes/(authed)/admin/+layout.svelte | 8 +- src/routes/(authed)/departments/+page.svelte | 2 +- .../add/[country]/[catalog]/+page.svelte | 846 ++++++ .../edit/[country]/[catalog]/+page.svelte | 2656 +++++++++++++++++ .../(authed)/sheet/overview/+page.server.ts | 11 + .../(authed)/sheet/overview/+page.svelte | 171 +- .../sheet/overview/[country]/+page.svelte | 279 ++ .../(authed)/tools/adv-upload/+page.svelte | 833 ++++++ .../tools/android-recipe/+page.svelte | 42 + src/routes/(authed)/tools/brew/+page.svelte | 583 +++- .../(authed)/tools/create-menu/+page.svelte | 1892 ++++++++++++ .../(authed)/tools/image-upload/+page.svelte | 442 +++ src/routes/api/adv-manifest/+server.ts | 42 + src/routes/api/adv-upload/+server.ts | 55 + src/routes/api/image-upload/+server.ts | 58 + src/routes/api/sheet/stream/+server.ts | 91 + vite.config.ts | 2 +- 44 files changed, 12421 insertions(+), 214 deletions(-) create mode 100644 bun.lock delete mode 100755 bun.lockb create mode 100644 src/lib/components/android-recipe-export-view.svelte create mode 100644 src/lib/core/services/androidRecipeExportService.ts create mode 100644 src/lib/core/services/sheetService.ts create mode 100644 src/lib/core/stores/genLayoutStore.ts create mode 100644 src/lib/core/stores/menuSaveStore.ts create mode 100644 src/lib/core/stores/sheetStore.ts create mode 100644 src/lib/core/types/catalogData.ts create mode 100644 src/lib/core/utils/productCode.ts create mode 100644 src/lib/data/productcode.txt create mode 100644 src/lib/workers/androidRecipeExport.worker.ts create mode 100644 src/routes/(authed)/sheet/add/[country]/[catalog]/+page.svelte create mode 100644 src/routes/(authed)/sheet/edit/[country]/[catalog]/+page.svelte create mode 100644 src/routes/(authed)/sheet/overview/+page.server.ts create mode 100644 src/routes/(authed)/sheet/overview/[country]/+page.svelte create mode 100644 src/routes/(authed)/tools/adv-upload/+page.svelte create mode 100644 src/routes/(authed)/tools/android-recipe/+page.svelte create mode 100644 src/routes/(authed)/tools/create-menu/+page.svelte create mode 100644 src/routes/(authed)/tools/image-upload/+page.svelte create mode 100644 src/routes/api/adv-manifest/+server.ts create mode 100644 src/routes/api/adv-upload/+server.ts create mode 100644 src/routes/api/image-upload/+server.ts create mode 100644 src/routes/api/sheet/stream/+server.ts diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..b36186e --- /dev/null +++ b/bun.lock @@ -0,0 +1,1097 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "supra-app", + "dependencies": { + "@battlefieldduck/xterm-svelte": "^2.2.1", + "@dnd-kit-svelte/core": "^0.0.11", + "@dnd-kit-svelte/svelte": "0.1.3", + "@dnd-kit/abstract": "^0.2.4", + "@dnd-kit/helpers": "^0.2.4", + "@tanstack/match-sorter-utils": "^8.19.4", + "@xterm/xterm": "^5.5.0", + "@yume-chan/adb": "^2.6.0", + "@yume-chan/adb-credential-web": "^2.1.0", + "@yume-chan/adb-daemon-webusb": "^2.3.2", + "@yume-chan/adb-scrcpy": "^2.3.2", + "@yume-chan/scrcpy": "^2.3.0", + "@yume-chan/stream-extra": "^2.5.3", + "animejs": "^4.3.6", + "firebase": "^12.14.0", + "idb": "^8.0.3", + "mode-watcher": "^1.1.0", + "usb": "^2.17.0", + "uuid": "^13.0.0", + "xterm-addon-fit": "^0.8.0", + "zod": "^4.3.6", + }, + "devDependencies": { + "@chromatic-com/storybook": "^4.1.3", + "@internationalized/date": "^3.12.0", + "@lucide/svelte": "^0.561.0", + "@storybook/addon-a11y": "^10.3.5", + "@storybook/addon-docs": "^10.3.5", + "@storybook/addon-svelte-csf": "^5.1.2", + "@storybook/addon-vitest": "^10.3.5", + "@storybook/sveltekit": "^10.3.5", + "@sveltejs/adapter-auto": "^7.0.1", + "@sveltejs/adapter-node": "^5.5.4", + "@sveltejs/kit": "^2.57.0", + "@sveltejs/vite-plugin-svelte": "^6.2.4", + "@tailwindcss/forms": "^0.5.11", + "@tailwindcss/typography": "^0.5.19", + "@tailwindcss/vite": "^4.2.2", + "@tanstack/table-core": "^8.21.3", + "@types/node": "^22.19.17", + "@vitest/browser": "^3.2.4", + "bits-ui": "^2.17.3", + "clsx": "^2.1.1", + "paneforge": "^1.0.2", + "playwright": "^1.59.1", + "prettier": "^3.8.1", + "prettier-plugin-svelte": "^3.5.1", + "prettier-plugin-tailwindcss": "^0.7.2", + "storybook": "^10.3.5", + "svelte": "^5.55.2", + "svelte-adapter-bun": "^1.0.1", + "svelte-check": "^4.4.6", + "svelte-sonner": "^1.1.0", + "tailwind-merge": "^3.5.0", + "tailwind-variants": "^3.2.2", + "tailwindcss": "^4.2.2", + "tw-animate-css": "^1.4.0", + "typescript": "^5.9.3", + "vaul-svelte": "^1.0.0-next.7", + "vite": "^7.3.2", + "vite-plugin-devtools-json": "^1.0.0", + "vitest": "^4.1.4", + "vitest-browser-svelte": "^2.1.0", + }, + }, + }, + "packages": { + "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="], + + "@battlefieldduck/xterm-svelte": ["@battlefieldduck/xterm-svelte@2.2.1", "", { "dependencies": { "@xterm/addon-attach": "^0.12.0", "@xterm/addon-clipboard": "^0.2.0", "@xterm/addon-fit": "^0.11.0", "@xterm/addon-image": "^0.9.0", "@xterm/addon-progress": "^0.2.0", "@xterm/addon-search": "^0.16.0", "@xterm/addon-serialize": "^0.14.0", "@xterm/addon-unicode11": "^0.9.0", "@xterm/addon-web-links": "^0.12.0", "@xterm/addon-webgl": "^0.19.0", "@xterm/xterm": "^6.0.0" }, "peerDependencies": { "svelte": "^5.46.4" } }, "sha512-Wy9w7upfHr0breWIiZUskvhQEmjST3quANMx8CwCLvE4lI3/4SRcz4E3ySFLdzYzpZi/4pQZvqAAM3zg15oaMQ=="], + + "@chromatic-com/storybook": ["@chromatic-com/storybook@4.1.3", "", { "dependencies": { "@neoconfetti/react": "^1.0.0", "chromatic": "^13.3.3", "filesize": "^10.0.12", "jsonfile": "^6.1.0", "strip-ansi": "^7.1.0" }, "peerDependencies": { "storybook": "^0.0.0-0 || ^9.0.0 || ^9.1.0-0 || ^9.2.0-0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0" } }, "sha512-hc0HO9GAV9pxqDE6fTVOV5KeLpTiCfV8Jrpk5ogKLiIgeq2C+NPjpt74YnrZTjiK8E19fYcMP+2WY9ZtX7zHmw=="], + + "@dnd-kit-svelte/accessibility": ["@dnd-kit-svelte/accessibility@0.0.11", "", { "dependencies": { "@dnd-kit-svelte/utilities": "latest", "esm-env": "^1.2.2", "runed": "^0.23.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-YkMjzabpnwELyeOSC2COV6EIGWZM8m4kgi/wIMwG0g/ZO3WWuzkMiLRLhYZqRkrEdpoz5tZnVclw+x/kcUjQHw=="], + + "@dnd-kit-svelte/core": ["@dnd-kit-svelte/core@0.0.11", "", { "dependencies": { "@dnd-kit-svelte/accessibility": "latest", "@dnd-kit-svelte/utilities": "latest", "runed": "^0.23.0", "svelte-toolbelt": "^0.7.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-bVqutZhSOk3nQsjwJ0SveUrPeBgUO/wajfon1AiKA8mxfQhaUG97CfoQLs7tLW3YxSkHkLPhYRw9WQat7OMB7g=="], + + "@dnd-kit-svelte/svelte": ["@dnd-kit-svelte/svelte@0.1.3", "", { "dependencies": { "@dnd-kit/abstract": "^0.1.21", "@dnd-kit/collision": "^0.1.21", "@dnd-kit/dom": "^0.1.21", "@dnd-kit/state": "^0.1.21" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-2Zz0y+YsvppwY1YwY4y7nHx3ge4nB4Zd0mgJOlGgDSM6Rz+CuRMIOq+fNq6Ah6giRl15xiXfXBTvbHITnXJYmg=="], + + "@dnd-kit-svelte/utilities": ["@dnd-kit-svelte/utilities@0.0.11", "", { "dependencies": { "svelte-toolbelt": "^0.7.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-Y9Z016gLdieHiAVoteSBa1tvrgug1/Rl5wdKw/2g1Fd4G0q9HWIyPe2evKrv8H5ePfcNegA1jN7fneDVmHd3pQ=="], + + "@dnd-kit/abstract": ["@dnd-kit/abstract@0.2.4", "", { "dependencies": { "@dnd-kit/geometry": "^0.2.4", "@dnd-kit/state": "^0.2.4", "tslib": "^2.6.2" } }, "sha512-1F3JQaD8upvTNpfDrhvec57cldTEaUggmVrVJZIpRTIdZR7trKqS7vBYZR1mW3qNjWsxC91eEQAXYWv0vxXXuw=="], + + "@dnd-kit/collision": ["@dnd-kit/collision@0.1.21", "", { "dependencies": { "@dnd-kit/abstract": "^0.1.21", "@dnd-kit/geometry": "^0.1.21", "tslib": "^2.6.2" } }, "sha512-9AJ4NbuwGDexxMCZXZyKdNQhbAe93p6C6IezQaDaWmdCqZHMHmC3+ul7pGefBQfOooSarGwIf8Bn182o9SMa1A=="], + + "@dnd-kit/dom": ["@dnd-kit/dom@0.1.21", "", { "dependencies": { "@dnd-kit/abstract": "^0.1.21", "@dnd-kit/collision": "^0.1.21", "@dnd-kit/geometry": "^0.1.21", "@dnd-kit/state": "^0.1.21", "tslib": "^2.6.2" } }, "sha512-6UDc1y2Y3oLQKArGlgCrZxz5pdEjRSiQujXOn5JdbuWvKqTdUR5RTYDeicr+y2sVm3liXjTqs3WlUoV+eqhqUQ=="], + + "@dnd-kit/geometry": ["@dnd-kit/geometry@0.2.4", "", { "dependencies": { "@dnd-kit/state": "^0.2.4", "tslib": "^2.6.2" } }, "sha512-S8wVj4rQIErimDQoRT0n1+lgxPvlO/gvzT9/1vzyyw1U4bB/S8avtrI+REVw/jH84c/dii+GwrrzVkDDoIluNw=="], + + "@dnd-kit/helpers": ["@dnd-kit/helpers@0.2.4", "", { "dependencies": { "@dnd-kit/abstract": "^0.2.4", "tslib": "^2.6.2" } }, "sha512-xKnkVPVH5YOeyerJGsqgzivTZq1lGSqnBiP5T+3G2jnM/KY5FCZBJSVcwNzFXcaIrMlj35N/sxlFHCX8kKgXEA=="], + + "@dnd-kit/state": ["@dnd-kit/state@0.1.21", "", { "dependencies": { "@preact/signals-core": "^1.10.0", "tslib": "^2.6.2" } }, "sha512-pdhntEPvn/QttcF295bOJpWiLsRqA/Iczh1ODOJUxGiR+E4GkYVz9VapNNm9gDq6ST0tr/e1Q2xBztUHlJqQgA=="], + + "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + + "@firebase/ai": ["@firebase/ai@2.13.0", "", { "dependencies": { "@firebase/app-check-interop-types": "0.3.4", "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x", "@firebase/app-types": "0.x" } }, "sha512-nJJDQKqjAcbkZdZGT/5WTVLrGZ+pYhWbwKC90nNzmvtoRTtnOJaNS34fhKSHQeB9SALgD2kxuWT5I4AkytdZ/Q=="], + + "@firebase/analytics": ["@firebase/analytics@0.10.22", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/installations": "0.6.22", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-8BSaq/QRGU1+xyi8L2PTLTJU7MH9aMA72RQdIxrbhWFauOZY9OXo8f2YDN/972xA8d588tlnNVEQ2Mo69pT9Ow=="], + + "@firebase/analytics-compat": ["@firebase/analytics-compat@0.2.28", "", { "dependencies": { "@firebase/analytics": "0.10.22", "@firebase/analytics-types": "0.8.4", "@firebase/component": "0.7.3", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-lIAlqUUbBu93FJMlQfslryQtBwwzdzvp23ePC6FNgymXk6Ook5v4Uvc0vdutvoIeqmyA3LfP0ZeRFK8+11kOOQ=="], + + "@firebase/analytics-types": ["@firebase/analytics-types@0.8.4", "", {}, "sha512-zQ+XTgkwH6CY/eUSHJRP7e4LxM30RCxlCmob5sy2axs25GE3Ny0XdgpDscMTHHQIGqWkxPXad4w2Mw9sCgT8zQ=="], + + "@firebase/app": ["@firebase/app@0.14.13", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "idb": "7.1.1", "tslib": "^2.1.0" } }, "sha512-H89Jeyp31+EZk9GPu6vaeL9mEmoXgM3nASB7UPBYYS/lqAks21mO1BU1dF8NbsVTL6tgGZkGUtiGJgxtDiwHkw=="], + + "@firebase/app-check": ["@firebase/app-check@0.11.4", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-G8EsbVJV9gSfoibx0dNoNOUrvr+PkL7J//+W/BST/oUassimkZeq9bjj3bKkB0pn4og5GMQ9qs7FefwP00kkgg=="], + + "@firebase/app-check-compat": ["@firebase/app-check-compat@0.4.4", "", { "dependencies": { "@firebase/app-check": "0.11.4", "@firebase/app-check-types": "0.5.4", "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-9iP0MvmaVagulNXmrca96U3tqNAI3j98wsC1z7rj62nnOTajlrHM//jjB9VoHqRw6/islMskp6RsKnM7vhLDqA=="], + + "@firebase/app-check-interop-types": ["@firebase/app-check-interop-types@0.3.4", "", {}, "sha512-zz3i6e13B8BfWiLy8MABtTh8aGIACgKbf9UVnyHcWs+yQzJXgQcl8A46b0zfaiJHdQ+niF0ouAfcpuf+3LMPQg=="], + + "@firebase/app-check-types": ["@firebase/app-check-types@0.5.4", "", {}, "sha512-xV7JsIyzVr15aA7f3Pi0rB9gdBuVubs89FGA8VkRYA4g0l78poADgdfrScgf7NndSg9mm7cR7PJyY0+t22KaGw=="], + + "@firebase/app-compat": ["@firebase/app-compat@0.5.13", "", { "dependencies": { "@firebase/app": "0.14.13", "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0" } }, "sha512-pn3FvXwUR34kWPccDQfCKsNZcM2wD1OS+J1jeEgzM1ZNXoxR2NaF6e5DjDuRrnTwR6LN2XQQt0IqE6yKmgpCQg=="], + + "@firebase/app-types": ["@firebase/app-types@0.9.5", "", { "dependencies": { "@firebase/logger": "0.5.1" } }, "sha512-YevqTjvo7Iujsa9Dwowmd6dSoElhzmD63ZSrq6bzjvQ6POjYgNjOFHLmNIgJs48eNO093NCERibuFnxbfOvU7A=="], + + "@firebase/auth": ["@firebase/auth@1.13.2", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x", "@react-native-async-storage/async-storage": "^2.2.0 || ^3.0.0" }, "optionalPeers": ["@react-native-async-storage/async-storage"] }, "sha512-B4w0iS7MxRg28oIh2fJFTE6cM0lYdBrW19eHpc42jqEcloUjlYyVrpPqZvqA4+v9KFEVSKEs2SfWyta7hbzkJQ=="], + + "@firebase/auth-compat": ["@firebase/auth-compat@0.6.7", "", { "dependencies": { "@firebase/auth": "1.13.2", "@firebase/auth-types": "0.13.1", "@firebase/component": "0.7.3", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-XgKnOgY1Siq7gylAmLkYtHAlRxNeWEAspH+nO3gJZJnfHqoTHbr9UjJ3nHNFALYXV5CfpQlyPROyB2ztySBHBQ=="], + + "@firebase/auth-interop-types": ["@firebase/auth-interop-types@0.2.5", "", {}, "sha512-1Li/YuBDBAXcKv7BzY4U28gontUmAaw53sYiqbaVOMCFb2lFKK/c3CGMUWqtwe7+TXrl3poWnTCL5umYBg85Eg=="], + + "@firebase/auth-types": ["@firebase/auth-types@0.13.1", "", { "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" } }, "sha512-0c1Mnid0uMDfGJHeUS4zfvBa4/CedJXotGy/n/NZJnBjwiJawt0ZYU+wH2VAVLiRCEfG2ncCkAX3yd1/2nrB7g=="], + + "@firebase/component": ["@firebase/component@0.7.3", "", { "dependencies": { "@firebase/util": "1.15.1", "tslib": "^2.1.0" } }, "sha512-wFofIaa2879ogD/WvkjYXJxRmfnL0scen6ORgaC3na1FNOR9ASIUANQdhqQcmWu/h77/pVHY7ch5flewa5Bcew=="], + + "@firebase/data-connect": ["@firebase/data-connect@0.7.1", "", { "dependencies": { "@firebase/auth-interop-types": "0.2.5", "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-2LbUU8mmSA63HknxQMmWHjpzuNLBKflvVwQc2tpoVKg0biWleNEJX031ELks0vzFs+dDjOUkCJR72RP6mQHFOg=="], + + "@firebase/database": ["@firebase/database@1.1.3", "", { "dependencies": { "@firebase/app-check-interop-types": "0.3.4", "@firebase/auth-interop-types": "0.2.5", "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "faye-websocket": "0.11.4", "tslib": "^2.1.0" } }, "sha512-XwWCa+E4TvNGpGwXrycLRNfdogADwFcvuhyow6wDWma9W54roaQIhe+4PM0KiLsIftBdSCGI7OKCXrdSRHbIhw=="], + + "@firebase/database-compat": ["@firebase/database-compat@2.1.4", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/database": "1.1.3", "@firebase/database-types": "1.0.20", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0" } }, "sha512-3pK35F1MAgmqFJQlf2nhQl44vtAXQO1uaCaQOEUI9kCRtLFqi7N+QRKR7lFZPg+xIZIyubgxQaxY69YgfZRZWg=="], + + "@firebase/database-types": ["@firebase/database-types@1.0.20", "", { "dependencies": { "@firebase/app-types": "0.9.5", "@firebase/util": "1.15.1" } }, "sha512-kegbOk/w8iU64pr0q6k2ItyNGjnQBMHFhwS7ohdWI4W+pc0/zhhdGXTdFj6X1oxItRjPoYOsSQmERgBkn/ihxw=="], + + "@firebase/firestore": ["@firebase/firestore@4.15.0", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "@firebase/webchannel-wrapper": "1.0.6", "@grpc/grpc-js": "~1.9.0", "@grpc/proto-loader": "^0.7.8", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-Fj9osqYkz2Rqr7kW3/A8BRd8CyJ7yA5K8YjhihRdyJWbL+FsELVcR6DpoCplrp1IyU+xeGgTubo1UOySXpY+EA=="], + + "@firebase/firestore-compat": ["@firebase/firestore-compat@0.4.10", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/firestore": "4.15.0", "@firebase/firestore-types": "3.0.4", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-yMP3FADDjikdrQv4YmvL4EkIny6Hw+N+a2O5T40rlHiniyMpRPxgYkKiFOvMZnsqKLqBVnKqCAElC0pa/IZtdw=="], + + "@firebase/firestore-types": ["@firebase/firestore-types@3.0.4", "", { "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" } }, "sha512-jGn+JSS4X9zZsrfu7Yw66v5YRdOLD1oyQh4USR0xWl4CUqV/DA6bNIXRPpxH/cUl3iVTNiP6MN7g+EL42A4qfA=="], + + "@firebase/functions": ["@firebase/functions@0.13.5", "", { "dependencies": { "@firebase/app-check-interop-types": "0.3.4", "@firebase/auth-interop-types": "0.2.5", "@firebase/component": "0.7.3", "@firebase/messaging-interop-types": "0.2.5", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-bWCx713f4kE/uFV7gdFOLBS7lDoiZj48MRkbAqe35gkXcCeWF4QjRNO07Jhmve7EJIoQOBczL29y2r8VRuN1kw=="], + + "@firebase/functions-compat": ["@firebase/functions-compat@0.4.5", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/functions": "0.13.5", "@firebase/functions-types": "0.6.4", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-10qlUXGY25G5/1g9UihqksPp2po+ZqSE7LEizsrdUP7vrTmkysXxGSZCDyojSEp6mQe/ecRDdDDI+z4XRdb4wQ=="], + + "@firebase/functions-types": ["@firebase/functions-types@0.6.4", "", {}, "sha512-zV6kgqtduR4rUAdC/ilS7kmb93XD7bEZoJDlVBZqlOw2uGGGCNBQBuleww2rr0Ulr3L9o2TDjumEt68/l1f9DQ=="], + + "@firebase/installations": ["@firebase/installations@0.6.22", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/util": "1.15.1", "idb": "7.1.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-ef6nn3GGQTdReCfotRMG77PJZu8CqEbiK5pEoBnM0gTu/Z9v0i/az2p3HABsa/1beQmmyh1OsOjf7P5+pgwdZw=="], + + "@firebase/installations-compat": ["@firebase/installations-compat@0.2.22", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/installations": "0.6.22", "@firebase/installations-types": "0.5.4", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-C/zpAuTP5S9OgKSPvXRupw3hoY/JZSlA1wFjD/Sb7LIQE0FNbcMdO8Y4KXVEkjVzma/DDDDIAzxEXqKMAzc88w=="], + + "@firebase/installations-types": ["@firebase/installations-types@0.5.4", "", { "peerDependencies": { "@firebase/app-types": "0.x" } }, "sha512-U2eFapdHwjb43Vx9o+Pmj4dFfvcHEK1IirEFLqMtWrTHvmdrS3gBpBD1kmJk/9HjsOtoHZxJ2Paoe79e+L1ZPg=="], + + "@firebase/logger": ["@firebase/logger@0.5.1", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-vZKLsqE1ABOy8OjQiE7cUTFn4gvaqlk88yp8N94Pk/sDpq61YqZGqmVFZTvOyflTwuYFcWirBdYGoJgbDaXKYQ=="], + + "@firebase/messaging": ["@firebase/messaging@0.13.0", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/installations": "0.6.22", "@firebase/messaging-interop-types": "0.2.5", "@firebase/util": "1.15.1", "idb": "7.1.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-GZoo0uGRvEbszo83xcgbjJp4FpkmBEr4l8Z4hi8gl+P1Spn/MTK3HapanMzSX4yUHuTEiF5hasWRxOaz+o5sxQ=="], + + "@firebase/messaging-compat": ["@firebase/messaging-compat@0.2.27", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/messaging": "0.13.0", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-JNOiu1PPgdHzEPEtoFiNxQuu0x9bm4bfETSQCpGfcTlgWkhlSK7uh7nlsjC10TQLUNgYetLmuutaYTh8aeYLVA=="], + + "@firebase/messaging-interop-types": ["@firebase/messaging-interop-types@0.2.5", "", {}, "sha512-tUEKnaAP2Y/MNIqgnriPpV6e5l13Vs/+p2yrd6NGlncPJT9O3a8muYZtdnWe+IJ4fgKLHJVC79n/asxk/N5Msw=="], + + "@firebase/performance": ["@firebase/performance@0.7.12", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/installations": "0.6.22", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0", "web-vitals": "^4.2.4" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-fe7nV8teUU3OBHlMUZ9Lw4gLhCW2k4m5Uc3pfWGV+fl8uwJQBGp9Q3lqsJ+HSrFu3Q2pJyLAgrClPGSKyDeYgQ=="], + + "@firebase/performance-compat": ["@firebase/performance-compat@0.2.25", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/performance": "0.7.12", "@firebase/performance-types": "0.2.4", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-q6NjTXpIPoFuUmCmMN/maCdTgzT6aExs9xZo+PxfVLj6uLVGvpyAD6XWjmcrb7jChsFBYbq7E5dyNDF7Zhy9kA=="], + + "@firebase/performance-types": ["@firebase/performance-types@0.2.4", "", {}, "sha512-kJSEk7b0uhpcPRyL4SQ/GPujLqk52XNKcXlnsKDbWGAb9vugcLvOU3u6zfEdwd+d8hWJb5S5ZizV1JFFI0nkKg=="], + + "@firebase/remote-config": ["@firebase/remote-config@0.8.4", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/installations": "0.6.22", "@firebase/logger": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-lslywR5lGvHWTu4z/MPoYs3UwS3CKdeY+ELXY87087VsOpBpkD+9Orra23tA9GW683arPTDOM3CM6eKmtiOO3g=="], + + "@firebase/remote-config-compat": ["@firebase/remote-config-compat@0.2.25", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", "@firebase/remote-config": "0.8.4", "@firebase/remote-config-types": "0.5.1", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-FnA5S4IxFJAAFrCnYzWlO0FCaizlYdqhe42ygFMA+wE/mUP+w36iXzHyKj1OO1A+2gyMFjeRHyg8HhkJ6c5vRA=="], + + "@firebase/remote-config-types": ["@firebase/remote-config-types@0.5.1", "", {}, "sha512-cX/1LT6KQwkXzck2eSzeKnuvXZCyr8qaPpDcikoJs7jmI+oBOXixpDLeDtWj1U6GNMkIoXrEDNoyT2Ypcyp5/A=="], + + "@firebase/storage": ["@firebase/storage@0.14.3", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-YX4/YL6P6/fufSSeGnVhjWddcIXbFq2cWIhMKFTZo1E/Rtcl2mJj/BYUQTwJfcE1Tl8un1FOya4L05jcSLN/Eg=="], + + "@firebase/storage-compat": ["@firebase/storage-compat@0.4.3", "", { "dependencies": { "@firebase/component": "0.7.3", "@firebase/storage": "0.14.3", "@firebase/storage-types": "0.8.4", "@firebase/util": "1.15.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-gruVqjtUGX8tEoeNbaWXZm0Zfcfcb7fvmDmBxV8yPAbWvExRnZYLO2+qw9idxNE7BvPXt5csyjSYHy//dAizxw=="], + + "@firebase/storage-types": ["@firebase/storage-types@0.8.4", "", { "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" } }, "sha512-BT7cwxJOx8SWwlQfrlC+bD/Sk3Cw+1odCi8UZNFNWTVZoPsBnA5W+mqtZzVnvsdJpXCFGSGQ7R7vOR6dtM/BRA=="], + + "@firebase/util": ["@firebase/util@1.15.1", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-LUdM4Wg7YM9Pq/49nGYySJA0CSQEKnGffFzWV8+6gXN7mGxn+FL1IqvFbuZUtAQcfZgHYDwCE1wwlK7rB7gl2g=="], + + "@firebase/webchannel-wrapper": ["@firebase/webchannel-wrapper@1.0.6", "", {}, "sha512-Vr/Mqu79dMwGRAyGbJ4uN4+BtXB3/mRTdzetD1daWNeG8QaWuzhhbG77GltO5c0yYmYls8i250iX73624GJd7Q=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + + "@grpc/grpc-js": ["@grpc/grpc-js@1.9.15", "", { "dependencies": { "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" } }, "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ=="], + + "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="], + + "@internationalized/date": ["@internationalized/date@3.12.1", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@lucide/svelte": ["@lucide/svelte@0.561.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-vofKV2UFVrKE6I4ewKJ3dfCXSV6iP6nWVmiM83MLjsU91EeJcEg7LoWUABLp/aOTxj1HQNbJD1f3g3L0JQgH9A=="], + + "@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@neoconfetti/react": ["@neoconfetti/react@1.0.0", "", {}, "sha512-klcSooChXXOzIm+SE5IISIAn3bYzYfPjbX7D7HoqZL84oAfgREeSg5vSIaSFH+DaGzzvImTyWe1OyrJ67vik4A=="], + + "@oxc-project/types": ["@oxc-project/types@0.129.0", "", {}, "sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg=="], + + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], + + "@preact/signals-core": ["@preact/signals-core@1.14.1", "", {}, "sha512-vxPpfXqrwUe9lpjqfYNjAF/0RF/eFGeLgdJzdmIIZjpOnTmGmAB4BjWone562mJGMRP4frU6iZ6ei3PDsu52Ng=="], + + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.5", "", {}, "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/inquire": ["@protobufjs/inquire@1.1.1", "", {}, "sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.1", "", {}, "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0", "", { "os": "linux", "cpu": "arm" }, "sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg=="], + + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0", "", { "os": "none", "cpu": "arm64" }, "sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig=="], + + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0", "", { "os": "win32", "cpu": "x64" }, "sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0", "", {}, "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ=="], + + "@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@29.0.2", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-S/ggWH1LU7jTyi9DxZOKyxpVd4hF/OZ0JrEbeLjXk/DFXwRny0tjD2c992zOUYQobLrVkRVMDdmHP16HKP7GRg=="], + + "@rollup/plugin-json": ["@rollup/plugin-json@6.1.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA=="], + + "@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg=="], + + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.3", "", { "os": "android", "cpu": "arm" }, "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.3", "", { "os": "android", "cpu": "arm64" }, "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.60.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.60.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.60.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.60.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.60.3", "", { "os": "linux", "cpu": "arm" }, "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.60.3", "", { "os": "linux", "cpu": "arm" }, "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.60.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.60.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.60.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.60.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.60.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.60.3", "", { "os": "linux", "cpu": "x64" }, "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.60.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.60.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.60.3", "", { "os": "none", "cpu": "arm64" }, "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.60.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.60.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.60.3", "", { "os": "win32", "cpu": "x64" }, "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.3", "", { "os": "win32", "cpu": "x64" }, "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@storybook/addon-a11y": ["@storybook/addon-a11y@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^10.3.6" } }, "sha512-cbwXIT5CeHZ9AFbTKQ6YB7Ct6TAl/kKOgALbvzzVtFfRvm51JYygGaiJaB7PbPWn9wgJP2olJcFt+erlEc6cRw=="], + + "@storybook/addon-docs": ["@storybook/addon-docs@10.3.6", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "10.3.6", "@storybook/icons": "^2.0.1", "@storybook/react-dom-shim": "10.3.6", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.3.6" } }, "sha512-TvIdADVPtauxW0LzXIpIv7X6GxwetorhyNh+6+7MHC27XSBCWVxxRUwL63YeLlHTuXsIk0quG3b1xgwVRzWOJA=="], + + "@storybook/addon-svelte-csf": ["@storybook/addon-svelte-csf@5.1.2", "", { "dependencies": { "@storybook/csf": "^0.1.13", "dedent": "^1.5.3", "es-toolkit": "^1.26.1", "esrap": "^1.2.2", "magic-string": "^0.30.12", "svelte-ast-print": "^0.4.0", "zimmerframe": "^1.1.2" }, "peerDependencies": { "@storybook/svelte": "^0.0.0-0 || ^8.2.0 || ^9.0.0 || ^9.1.0-0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0", "@sveltejs/vite-plugin-svelte": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "storybook": "^0.0.0-0 || ^8.2.0 || ^9.0.0 || ^9.1.0-0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0", "svelte": "^5.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-NpImknEb48J7yr/ArTYpvhDSvGUrgm5Nuybu9PCicjSKTACsXX7cln2R19572ORtns399RTE+t20BBOKxSPm2g=="], + + "@storybook/addon-vitest": ["@storybook/addon-vitest@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1" }, "peerDependencies": { "@vitest/browser": "^3.0.0 || ^4.0.0", "@vitest/browser-playwright": "^4.0.0", "@vitest/runner": "^3.0.0 || ^4.0.0", "storybook": "^10.3.6", "vitest": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["@vitest/browser", "@vitest/browser-playwright", "@vitest/runner", "vitest"] }, "sha512-HXj7RrPJY+xzoNjL+xZu2oLw1fI5BA87Noh1NAXMPuECHR5R5fuRM/tTsJuIGXHFMO06FjSi/rekDIfCj1fL4w=="], + + "@storybook/builder-vite": ["@storybook/builder-vite@10.3.6", "", { "dependencies": { "@storybook/csf-plugin": "10.3.6", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.3.6", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-gpvR/sE4BcrFtmQZ+Ker7zD23oQzoVeqD9nF6cK6yzY+Q0svJXyX2EPmFG4y+EwygD5/vNzDpP84gGMut8VRwg=="], + + "@storybook/csf": ["@storybook/csf@0.1.13", "", { "dependencies": { "type-fest": "^2.19.0" } }, "sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q=="], + + "@storybook/csf-plugin": ["@storybook/csf-plugin@10.3.6", "", { "dependencies": { "unplugin": "^2.3.5" }, "peerDependencies": { "esbuild": "*", "rollup": "*", "storybook": "^10.3.6", "vite": "*", "webpack": "*" }, "optionalPeers": ["esbuild", "rollup", "vite", "webpack"] }, "sha512-9kBf7VRdRqTSIYo+rPtVn5yjYYyK8kP2QhEYx3oiXvfwy4RexmbJnhk/tXa/lNiTqukA1TqaWQ2+5MqF4fu6YQ=="], + + "@storybook/global": ["@storybook/global@5.0.0", "", {}, "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ=="], + + "@storybook/icons": ["@storybook/icons@2.0.2", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw=="], + + "@storybook/react-dom-shim": ["@storybook/react-dom-shim@10.3.6", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "storybook": "^10.3.6" } }, "sha512-/Tu1gPu+Fw+zOnAGmxRmOD30FX3a04LxcTAKflEtdpmtIMVR5bA3qpjy+f5YhoyDCecbXyKmL1OeIU2FIIZHqQ=="], + + "@storybook/svelte": ["@storybook/svelte@10.3.6", "", { "dependencies": { "ts-dedent": "^2.0.0", "type-fest": "~2.19" }, "peerDependencies": { "storybook": "^10.3.6", "svelte": "^5.0.0" } }, "sha512-XE+wNIiztpX6SapuJjYOgZajYWKDMDy/4LVbcqqypOoiYXnO/YOO2p9RdDgD8ta+J88Nap+/qiP7rBbzKOOrOA=="], + + "@storybook/svelte-vite": ["@storybook/svelte-vite@10.3.6", "", { "dependencies": { "@storybook/builder-vite": "10.3.6", "@storybook/svelte": "10.3.6", "magic-string": "^0.30.0", "svelte2tsx": "^0.7.44", "typescript": "^4.9.4 || ^5.0.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "storybook": "^10.3.6", "svelte": "^5.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-R+Z0TMqLe9XI7yJyfUel9qeZCh2pXUCl4C7SVWec53j9q0qEVcWVleh4Yob1p0dL0cBNMfU5bQN6d56nKVrwTA=="], + + "@storybook/sveltekit": ["@storybook/sveltekit@10.3.6", "", { "dependencies": { "@storybook/builder-vite": "10.3.6", "@storybook/svelte": "10.3.6", "@storybook/svelte-vite": "10.3.6" }, "peerDependencies": { "storybook": "^10.3.6", "svelte": "^5.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-pyQxMcJRhirtF2aWUNEfuSM5MCg4FXcis0Xk9duHIizOQ3pT0rPcY533g9EByzmi3Rm9xYOZVRKllla+G0kV1A=="], + + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="], + + "@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@7.0.1", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ=="], + + "@sveltejs/adapter-node": ["@sveltejs/adapter-node@5.5.4", "", { "dependencies": { "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.0", "rollup": "^4.59.0" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0" } }, "sha512-45X92CXW+2J8ZUzPv3eLlKWEzINKiiGeFWTjyER4ZN4sGgNoaoeSkCY/QYNxHpPXy71QPsctwccBo9jJs0ySPQ=="], + + "@sveltejs/kit": ["@sveltejs/kit@2.59.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.6.4", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-d8OON70AphLdDesuTIl//M2O6fRTIicX8aYv8vhCiYEhTTI2OboKqey0Hu1A4VFhqwgqtq0vKDmPFGkw8kKmgw=="], + + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA=="], + + "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.2", "", { "dependencies": { "obug": "^2.1.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig=="], + + "@swc/helpers": ["@swc/helpers@0.5.21", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg=="], + + "@tailwindcss/forms": ["@tailwindcss/forms@0.5.11", "", { "dependencies": { "mini-svg-data-uri": "^1.2.3" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" } }, "sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.2.4", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.4" } }, "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.4", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.4", "@tailwindcss/oxide-darwin-arm64": "4.2.4", "@tailwindcss/oxide-darwin-x64": "4.2.4", "@tailwindcss/oxide-freebsd-x64": "4.2.4", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", "@tailwindcss/oxide-linux-x64-musl": "4.2.4", "@tailwindcss/oxide-wasm32-wasi": "4.2.4", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" } }, "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.4", "", { "os": "android", "cpu": "arm64" }, "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.4", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw=="], + + "@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.2.4", "", { "dependencies": { "@tailwindcss/node": "4.2.4", "@tailwindcss/oxide": "4.2.4", "tailwindcss": "4.2.4" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw=="], + + "@tanstack/match-sorter-utils": ["@tanstack/match-sorter-utils@8.19.4", "", { "dependencies": { "remove-accents": "0.5.0" } }, "sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg=="], + + "@tanstack/table-core": ["@tanstack/table-core@8.21.3", "", {}, "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg=="], + + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + + "@testing-library/jest-dom": ["@testing-library/jest-dom@6.9.1", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA=="], + + "@testing-library/svelte-core": ["@testing-library/svelte-core@1.0.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0" } }, "sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ=="], + + "@testing-library/user-event": ["@testing-library/user-event@14.6.1", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], + + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + + "@types/node": ["@types/node@22.19.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ=="], + + "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + + "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="], + + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + + "@types/w3c-web-usb": ["@types/w3c-web-usb@1.0.14", "", {}, "sha512-Qu3Nn6JFuF4+sHKYl+IcX9vYiI40ogleXzFFSxoE1W94rG98o/kXs8uJ0QSfFzuwBCZWlGfUGpPkgwuuX4PchA=="], + + "@vitest/browser": ["@vitest/browser@3.2.4", "", { "dependencies": { "@testing-library/dom": "^10.4.0", "@testing-library/user-event": "^14.6.1", "@vitest/mocker": "3.2.4", "@vitest/utils": "3.2.4", "magic-string": "^0.30.17", "sirv": "^3.0.1", "tinyrainbow": "^2.0.0", "ws": "^8.18.2" }, "peerDependencies": { "playwright": "*", "vitest": "3.2.4", "webdriverio": "^7.0.0 || ^8.0.0 || ^9.0.0" }, "optionalPeers": ["playwright", "webdriverio"] }, "sha512-tJxiPrWmzH8a+w9nLKlQMzAKX/7VjFs50MWgcAj7p9XQ7AQ9/35fByFYptgPELyLw+0aixTnC4pUWV+APcZ/kw=="], + + "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + + "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@4.1.5", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g=="], + + "@vitest/runner": ["@vitest/runner@4.1.5", "", { "dependencies": { "@vitest/utils": "4.1.5", "pathe": "^2.0.3" } }, "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ=="], + + "@vitest/snapshot": ["@vitest/snapshot@4.1.5", "", { "dependencies": { "@vitest/pretty-format": "4.1.5", "@vitest/utils": "4.1.5", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ=="], + + "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + + "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + + "@webcontainer/env": ["@webcontainer/env@1.1.1", "", {}, "sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng=="], + + "@xterm/addon-attach": ["@xterm/addon-attach@0.12.0", "", {}, "sha512-1lxvXM4JYSm60lbFmE8WMOy2oF2ip3Ye8jWorSAmwy7x8FiC53netEJ5RguL8+FSRj79MUQsNCb2hprY2QA2ig=="], + + "@xterm/addon-clipboard": ["@xterm/addon-clipboard@0.2.0", "", { "dependencies": { "js-base64": "^3.7.5" } }, "sha512-Dl31BCtBhLaUEECUbEiVcCLvLBbaeGYdT7NofB8OJkGTD3MWgBsaLjXvfGAD4tQNHhm6mbKyYkR7XD8kiZsdNg=="], + + "@xterm/addon-fit": ["@xterm/addon-fit@0.11.0", "", {}, "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g=="], + + "@xterm/addon-image": ["@xterm/addon-image@0.9.0", "", {}, "sha512-oYWA8/QAr5/Emwl1xL7WCoOqeG3IZfpzEz/OVq1j4Oi9934TQmHiyubClikRf0D/jL3JNiNuz/Lsqx0kXQ02BA=="], + + "@xterm/addon-progress": ["@xterm/addon-progress@0.2.0", "", {}, "sha512-94uxxYyv30z3+6QIqJhCgALrzZfH7z2ounuZWQvb5Lp8dA7bWZmsUZGi5V7lKsq3Fyif4hTbaxq8YoCsQRtXgg=="], + + "@xterm/addon-search": ["@xterm/addon-search@0.16.0", "", {}, "sha512-9OeuBFu0/uZJPu+9AHKY6g/w0Czyb/Ut0A5t79I4ULoU4IfU5BEpPFVGQxP4zTTMdfZEYkVIRYbHBX1xWwjeSA=="], + + "@xterm/addon-serialize": ["@xterm/addon-serialize@0.14.0", "", {}, "sha512-uteyTU1EkrQa2Ux6P/uFl2fzmXI46jy5uoQMKEOM0fKTyiW7cSn0WrFenHm5vO5uEXX/GpwW/FgILvv3r0WbkA=="], + + "@xterm/addon-unicode11": ["@xterm/addon-unicode11@0.9.0", "", {}, "sha512-FxDnYcyuXhNl+XSqGZL/t0U9eiNb/q3EWT5rYkQT/zuig8Gz/VagnQANKHdDWFM2lTMk9ly0EFQxxxtZUoRetw=="], + + "@xterm/addon-web-links": ["@xterm/addon-web-links@0.12.0", "", {}, "sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw=="], + + "@xterm/addon-webgl": ["@xterm/addon-webgl@0.19.0", "", {}, "sha512-b3fMOsyLVuCeNJWxolACEUED0vm7qC0cy4wRvf3oURSzDTYVQiGPhTnhWZwIHdvC48Y+oLhvYXnY4XDXPoJo6A=="], + + "@xterm/xterm": ["@xterm/xterm@5.5.0", "", {}, "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="], + + "@yume-chan/adb": ["@yume-chan/adb@2.6.0", "", { "dependencies": { "@yume-chan/async": "^4.1.3", "@yume-chan/event": "^2.0.0", "@yume-chan/no-data-view": "^2.0.0", "@yume-chan/stream-extra": "^2.5.3", "@yume-chan/struct": "^2.3.2" } }, "sha512-1bM4/YwLUr7UHkvC3pe5fIPNNIgOQ97nUIvC4ooPZwRKG9pyHG39EihivZVmu3pm28XwHSGh5PC3sQi4ycoUXA=="], + + "@yume-chan/adb-credential-web": ["@yume-chan/adb-credential-web@2.1.0", "", { "dependencies": { "@yume-chan/adb": "^2.1.0" } }, "sha512-ps5XWt+xxm6Mi9eF80ePprPJ/uRFMhfR437betQE8hixrjdZpkZgMlqZU6GlnuebMi0djrbP8GWB49jJekJVYA=="], + + "@yume-chan/adb-daemon-webusb": ["@yume-chan/adb-daemon-webusb@2.3.2", "", { "dependencies": { "@types/w3c-web-usb": "^1.0.12", "@yume-chan/adb": "^2.3.1", "@yume-chan/event": "^2.0.0", "@yume-chan/stream-extra": "^2.1.0", "@yume-chan/struct": "^2.3.2" } }, "sha512-5ANaqsRJWFc5kKMnGALjYCrSXo5eC4u51dvYppvuBCLVjv12n7he2TPy5yEZ3XtlToRFS0kWCQC0XS8M60lRSA=="], + + "@yume-chan/adb-scrcpy": ["@yume-chan/adb-scrcpy@2.3.2", "", { "dependencies": { "@yume-chan/adb": "^2.3.1", "@yume-chan/async": "^4.1.3", "@yume-chan/event": "^2.0.0", "@yume-chan/scrcpy": "^2.3.0", "@yume-chan/stream-extra": "^2.1.0", "@yume-chan/struct": "^2.3.2" } }, "sha512-wg45dep+dYy4MAVMtsooPAo/aXKiGbpGHzcQWoHnl4uE4YJKrZK0sK5L++rPNM0WbwlPdSpZ32jMJT+i0u1Mdw=="], + + "@yume-chan/async": ["@yume-chan/async@4.1.3", "", {}, "sha512-0vzhNJMkWUPyjKzUK4rqHEeCU6YQtF78RsB1kFRB6Y2BLupmEQNxcSb0mjKabPL9jZpCCiLa5KL8oTOJClUVaw=="], + + "@yume-chan/event": ["@yume-chan/event@2.0.0", "", { "dependencies": { "@yume-chan/async": "^4.0.2" } }, "sha512-z56MDOcX1QlgLUCuA6th3r10negVb7A3gzY//TwSC9ZOvzuRlrAqXcxZf1T3hHfNMk/NFO9RIgQgegXYSfaqLw=="], + + "@yume-chan/no-data-view": ["@yume-chan/no-data-view@2.0.0", "", {}, "sha512-0GRJrrt6wtZlbiE92jocHOnaAvjQ+Y7xwwhwOPqLkwf90Kj1JIHJ5Zh4wJVQSQIkzfRSOpM+jeEQdC2K15snlA=="], + + "@yume-chan/scrcpy": ["@yume-chan/scrcpy@2.3.0", "", { "dependencies": { "@yume-chan/async": "^4.1.3", "@yume-chan/no-data-view": "^2.0.0", "@yume-chan/stream-extra": "^2.1.0", "@yume-chan/struct": "^2.0.1" } }, "sha512-rpsNvy7H8mf6gVsWYRSkIXmk0wVsP3XOdw3kASokwZkSWtygB2Z1wjoPaEAvaayVCjsz+y/YfG35TOcykEihGw=="], + + "@yume-chan/stream-extra": ["@yume-chan/stream-extra@2.5.3", "", { "dependencies": { "@yume-chan/async": "^4.1.3", "@yume-chan/struct": "^2.3.2" } }, "sha512-eRZcl0+SMIt/cAp9UZfBbh74mYe+DZf5l3C6eX8zYYA1cx71nZce2NCf8Vp+/XNw3UtPaEz6IiES9o7Ax5xJdg=="], + + "@yume-chan/struct": ["@yume-chan/struct@2.3.2", "", { "dependencies": { "@yume-chan/async": "^4.1.3", "@yume-chan/no-data-view": "^2.0.0" } }, "sha512-afoCnSKV+5HRK7e4innVd9YTYDyNWdjA1CVQa1j8rWYnmr7HGZfdkHMQr+AESLk6GxWFMzOIPQIG6nYOTGMFIw=="], + + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "animejs": ["animejs@4.4.1", "", {}, "sha512-XimhJw4vPb6tAd6Zaoa6BaspJ8UteQMKvL7Pvrt6MW/6u2UvJs0TB/LLqR1sigaiYvpVPC3Tokr9emcCMhWFhw=="], + + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], + + "axe-core": ["axe-core@4.11.4", "", {}, "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "bits-ui": ["bits-ui@2.18.1", "", { "dependencies": { "@floating-ui/core": "^1.7.1", "@floating-ui/dom": "^1.7.1", "esm-env": "^1.1.2", "runed": "^0.35.1", "svelte-toolbelt": "^0.10.6", "tabbable": "^6.2.0" }, "peerDependencies": { "@internationalized/date": "^3.8.1", "svelte": "^5.33.0" } }, "sha512-KkemzKFH4T3gt3H+P86JcnAWExjByv/6vlwjm/BoCwTPHu03yiCdxbghdJLvFReQTe0acCAiRcKfmixxD6XvlA=="], + + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], + + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + + "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "chromatic": ["chromatic@13.3.5", "", { "peerDependencies": { "@chromatic-com/cypress": "^0.*.* || ^1.0.0", "@chromatic-com/playwright": "^0.*.* || ^1.0.0" }, "optionalPeers": ["@chromatic-com/cypress", "@chromatic-com/playwright"], "bin": { "chroma": "dist/bin.js", "chromatic": "dist/bin.js", "chromatic-cli": "dist/bin.js" } }, "sha512-MzPhxpl838qJUo0A55osCF2ifwPbjcIPeElr1d4SHcjnHoIcg7l1syJDrAYK/a+PcCBrOGi06jPNpQAln5hWgw=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], + + "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "dedent": ["dedent@1.7.2", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA=="], + + "dedent-js": ["dedent-js@1.0.1", "", {}, "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ=="], + + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], + + "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="], + + "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "devalue": ["devalue@5.8.0", "", {}, "sha512-2zA9pFEsnp7vWBZbXF5JAgAq0fsUIt/1XPbRiAmRV3lp/2C3upzH+sADiyy66aFCihoLEsrQHxNM5w1gIDfsBg=="], + + "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "enhanced-resolve": ["enhanced-resolve@5.21.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-8p7DUVq6XJnZEz9W4oSwiwycxBIjHjRzYb3Je3zVN+geKTRQKzAkR/K4PBExlS0090d9nshak6phMUxr3PDjmQ=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="], + + "es-toolkit": ["es-toolkit@1.46.1", "", {}, "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ=="], + + "esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], + + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "esrap": ["esrap@1.4.9", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g=="], + + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + + "faye-websocket": ["faye-websocket@0.11.4", "", { "dependencies": { "websocket-driver": ">=0.5.1" } }, "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "filesize": ["filesize@10.1.6", "", {}, "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w=="], + + "firebase": ["firebase@12.14.0", "", { "dependencies": { "@firebase/ai": "2.13.0", "@firebase/analytics": "0.10.22", "@firebase/analytics-compat": "0.2.28", "@firebase/app": "0.14.13", "@firebase/app-check": "0.11.4", "@firebase/app-check-compat": "0.4.4", "@firebase/app-compat": "0.5.13", "@firebase/app-types": "0.9.5", "@firebase/auth": "1.13.2", "@firebase/auth-compat": "0.6.7", "@firebase/data-connect": "0.7.1", "@firebase/database": "1.1.3", "@firebase/database-compat": "2.1.4", "@firebase/firestore": "4.15.0", "@firebase/firestore-compat": "0.4.10", "@firebase/functions": "0.13.5", "@firebase/functions-compat": "0.4.5", "@firebase/installations": "0.6.22", "@firebase/installations-compat": "0.2.22", "@firebase/messaging": "0.13.0", "@firebase/messaging-compat": "0.2.27", "@firebase/performance": "0.7.12", "@firebase/performance-compat": "0.2.25", "@firebase/remote-config": "0.8.4", "@firebase/remote-config-compat": "0.2.25", "@firebase/storage": "0.14.3", "@firebase/storage-compat": "0.4.3", "@firebase/util": "1.15.1" } }, "sha512-aEZ/lniDR1hOCYpx/x/V8Nrrqq9pepKDNkqP/4WGZFC69gTv6F59Z4/54W/SUP4L/hFlrRNmWj35aweQq+IHow=="], + + "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="], + + "http-parser-js": ["http-parser-js@0.5.10", "", {}, "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA=="], + + "idb": ["idb@8.0.3", "", {}, "sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg=="], + + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + + "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], + + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], + + "is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="], + + "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], + + "is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], + + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + + "js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], + + "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], + + "mini-svg-data-uri": ["mini-svg-data-uri@1.4.4", "", { "bin": { "mini-svg-data-uri": "cli.js" } }, "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg=="], + + "mode-watcher": ["mode-watcher@1.1.0", "", { "dependencies": { "runed": "^0.25.0", "svelte-toolbelt": "^0.7.1" }, "peerDependencies": { "svelte": "^5.27.0" } }, "sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g=="], + + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], + + "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], + + "node-addon-api": ["node-addon-api@8.7.0", "", {}, "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA=="], + + "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], + + "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], + + "open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], + + "paneforge": ["paneforge@1.0.2", "", { "dependencies": { "runed": "^0.23.4", "svelte-toolbelt": "^0.9.2" }, "peerDependencies": { "svelte": "^5.29.0" } }, "sha512-KzmIXQH1wCfwZ4RsMohD/IUtEjVhteR+c+ulb/CHYJHX8SuDXoJmChtsc/Xs5Wl8NHS4L5Q7cxL8MG40gSU1bA=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "playwright": ["playwright@1.59.1", "", { "dependencies": { "playwright-core": "1.59.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw=="], + + "playwright-core": ["playwright-core@1.59.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg=="], + + "postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="], + + "postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], + + "prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="], + + "prettier-plugin-svelte": ["prettier-plugin-svelte@3.5.1", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-65+fr5+cgIKWKiqM1Doum4uX6bY8iFCdztvvp2RcF+AJoieaw9kJOFMNcJo/bkmKYsxFaM9OsVZK/gWauG/5mg=="], + + "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.4", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-UKii4RjY05SNt/WQi6/NcOn/LsT0/ILLXsxygjbRg5/YZelsSu5jTqorYHPDGq4nZy5q5hpCu+XdGZ1xaJEQgw=="], + + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + + "protobufjs": ["protobufjs@7.5.6", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.1", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-M71sTMB146U3u0di3yup8iM+zv8yPRNQVr1KK4tyBitl3qFvEGucq/rGDRShD2rsJhtN02RJaJ7j5X5hmy8SJg=="], + + "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], + + "react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], + + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], + + "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], + + "remove-accents": ["remove-accents@0.5.0", "", {}, "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], + + "rolldown": ["rolldown@1.0.0", "", { "dependencies": { "@oxc-project/types": "=0.129.0", "@rolldown/pluginutils": "1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0", "@rolldown/binding-darwin-arm64": "1.0.0", "@rolldown/binding-darwin-x64": "1.0.0", "@rolldown/binding-freebsd-x64": "1.0.0", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0", "@rolldown/binding-linux-arm64-gnu": "1.0.0", "@rolldown/binding-linux-arm64-musl": "1.0.0", "@rolldown/binding-linux-ppc64-gnu": "1.0.0", "@rolldown/binding-linux-s390x-gnu": "1.0.0", "@rolldown/binding-linux-x64-gnu": "1.0.0", "@rolldown/binding-linux-x64-musl": "1.0.0", "@rolldown/binding-openharmony-arm64": "1.0.0", "@rolldown/binding-wasm32-wasi": "1.0.0", "@rolldown/binding-win32-arm64-msvc": "1.0.0", "@rolldown/binding-win32-x64-msvc": "1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA=="], + + "rollup": ["rollup@4.60.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.3", "@rollup/rollup-android-arm64": "4.60.3", "@rollup/rollup-darwin-arm64": "4.60.3", "@rollup/rollup-darwin-x64": "4.60.3", "@rollup/rollup-freebsd-arm64": "4.60.3", "@rollup/rollup-freebsd-x64": "4.60.3", "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", "@rollup/rollup-linux-arm-musleabihf": "4.60.3", "@rollup/rollup-linux-arm64-gnu": "4.60.3", "@rollup/rollup-linux-arm64-musl": "4.60.3", "@rollup/rollup-linux-loong64-gnu": "4.60.3", "@rollup/rollup-linux-loong64-musl": "4.60.3", "@rollup/rollup-linux-ppc64-gnu": "4.60.3", "@rollup/rollup-linux-ppc64-musl": "4.60.3", "@rollup/rollup-linux-riscv64-gnu": "4.60.3", "@rollup/rollup-linux-riscv64-musl": "4.60.3", "@rollup/rollup-linux-s390x-gnu": "4.60.3", "@rollup/rollup-linux-x64-gnu": "4.60.3", "@rollup/rollup-linux-x64-musl": "4.60.3", "@rollup/rollup-openbsd-x64": "4.60.3", "@rollup/rollup-openharmony-arm64": "4.60.3", "@rollup/rollup-win32-arm64-msvc": "4.60.3", "@rollup/rollup-win32-ia32-msvc": "4.60.3", "@rollup/rollup-win32-x64-gnu": "4.60.3", "@rollup/rollup-win32-x64-msvc": "4.60.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A=="], + + "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], + + "runed": ["runed@0.35.1", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0" }, "optionalPeers": ["@sveltejs/kit"] }, "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q=="], + + "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="], + + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "set-cookie-parser": ["set-cookie-parser@3.1.0", "", {}, "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="], + + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="], + + "storybook": ["storybook@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "prettier": "^2 || ^3", "vite-plus": "^0.1.15" }, "optionalPeers": ["prettier", "vite-plus"], "bin": "./dist/bin/dispatcher.js" }, "sha512-vbSz7g/1rGMC1uAULqMZjALkIuLu2QABqfhRYhyr/11kzyesi+vAmwyJLukZP1FfecxGOgMwOh6GS0YsGpHAvQ=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], + + "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "svelte": ["svelte@5.55.5", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.4", "esm-env": "^1.2.1", "esrap": "^2.2.4", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-2uCs/LZ9us+AktdzYJM8OcxQ8qnPS1kpaO7syGT/MgO+6Qr1Ybl+TqPq+97u7PHqmmMlye5ZkoyXONy5mjjAbw=="], + + "svelte-adapter-bun": ["svelte-adapter-bun@1.0.1", "", { "dependencies": { "rolldown": "^1.0.0-beta.38" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0", "typescript": "^5" } }, "sha512-tNOvfm8BGgG+rmEA7hkmqtq07v7zoo4skLQc+hIoQ79J+1fkEMpJEA2RzCIe3aPc8JdrsMJkv3mpiZPMsgahjA=="], + + "svelte-ast-print": ["svelte-ast-print@0.4.2", "", { "dependencies": { "esrap": "1.2.2", "zimmerframe": "1.1.2" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-hRHHufbJoArFmDYQKCpCvc0xUuIEfwYksvyLYEQyH+1xb5LD5sM/IthfooCdXZQtOIqXz6xm7NmaqdfwG4kh6w=="], + + "svelte-check": ["svelte-check@4.4.8", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-67adfgBox5eNSNIvIIwgFizKGdcRrGpiMoNO2obHcYuLz7iTa8Xgm/NGU3ntMFnNm8K1grFOIG6HhMLX/vcN8w=="], + + "svelte-sonner": ["svelte-sonner@1.1.1", "", { "dependencies": { "runed": "^0.28.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-5cd3p7wa4cq0NsqslMwdlPb7x1JglEZ/GKrLePWNr5bCxR1nagAVrY01FRFrXfUGs41miLt3C327+8XJo5BzZw=="], + + "svelte-toolbelt": ["svelte-toolbelt@0.10.6", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.35.1", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.30.2" } }, "sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ=="], + + "svelte2tsx": ["svelte2tsx@0.7.55", "", { "dependencies": { "dedent-js": "^1.0.1", "scule": "^1.3.0" }, "peerDependencies": { "svelte": "^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0", "typescript": "^4.9.4 || ^5.0.0 || ^6.0.0" } }, "sha512-JWzgeM3lqySRNfqcsesvVEh8LhTWBxQJ9RMjzJ+VepdmXtVnNd0SbtGctG6+/fbHq0N6mhwSd823gszw9JHeGQ=="], + + "tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="], + + "tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="], + + "tailwind-variants": ["tailwind-variants@3.2.2", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge"] }, "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg=="], + + "tailwindcss": ["tailwindcss@4.2.4", "", {}, "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA=="], + + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], + + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@1.1.2", "", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="], + + "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], + + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], + + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], + + "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + + "type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], + + "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], + + "usb": ["usb@2.17.0", "", { "dependencies": { "@types/w3c-web-usb": "^1.0.6", "node-addon-api": "^8.0.0", "node-gyp-build": "^4.5.0" } }, "sha512-UuFgrlglgDn5ll6d5l7kl3nDb2Yx43qLUGcDq+7UNLZLtbNug0HZBb2Xodhgx2JZB1LqvU+dOGqLEeYUeZqsHg=="], + + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "uuid": ["uuid@13.0.2", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw=="], + + "vaul-svelte": ["vaul-svelte@1.0.0-next.7", "", { "dependencies": { "runed": "^0.23.2", "svelte-toolbelt": "^0.7.1" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-7zN7Bi3dFQixvvbUJY9uGDe7Ws/dGZeBQR2pXdXmzQiakjrxBvWo0QrmsX3HK+VH+SZOltz378cmgmCS9f9rSg=="], + + "vite": ["vite@7.3.3", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA=="], + + "vite-plugin-devtools-json": ["vite-plugin-devtools-json@1.0.0", "", { "dependencies": { "uuid": "^11.1.0" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-MobvwqX76Vqt/O4AbnNMNWoXWGrKUqZbphCUle/J2KXH82yKQiunOeKnz/nqEPosPsoWWPP9FtNuPBSYpiiwkw=="], + + "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], + + "vitest": ["vitest@4.1.5", "", { "dependencies": { "@vitest/expect": "4.1.5", "@vitest/mocker": "4.1.5", "@vitest/pretty-format": "4.1.5", "@vitest/runner": "4.1.5", "@vitest/snapshot": "4.1.5", "@vitest/spy": "4.1.5", "@vitest/utils": "4.1.5", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.5", "@vitest/browser-preview": "4.1.5", "@vitest/browser-webdriverio": "4.1.5", "@vitest/coverage-istanbul": "4.1.5", "@vitest/coverage-v8": "4.1.5", "@vitest/ui": "4.1.5", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg=="], + + "vitest-browser-svelte": ["vitest-browser-svelte@2.1.1", "", { "dependencies": { "@testing-library/svelte-core": "^1.0.0" }, "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "vitest": "^4.0.0" } }, "sha512-qbunYRSm+N92r9bfTkdDTpBZESLmp4QFz2SluV3n/x8U7ysosfeXYJZ4vXbJ0Y0LzoqqDnV5LHprmFgn4Eo+Ug=="], + + "web-vitals": ["web-vitals@4.2.4", "", {}, "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw=="], + + "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], + + "websocket-driver": ["websocket-driver@0.7.4", "", { "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg=="], + + "websocket-extensions": ["websocket-extensions@0.1.4", "", {}, "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], + + "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], + + "xterm": ["xterm@5.3.0", "", {}, "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg=="], + + "xterm-addon-fit": ["xterm-addon-fit@0.8.0", "", { "peerDependencies": { "xterm": "^5.0.0" } }, "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], + + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "@battlefieldduck/xterm-svelte/@xterm/xterm": ["@xterm/xterm@6.0.0", "", {}, "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg=="], + + "@dnd-kit-svelte/accessibility/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], + + "@dnd-kit-svelte/core/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], + + "@dnd-kit-svelte/core/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="], + + "@dnd-kit-svelte/svelte/@dnd-kit/abstract": ["@dnd-kit/abstract@0.1.21", "", { "dependencies": { "@dnd-kit/geometry": "^0.1.21", "@dnd-kit/state": "^0.1.21", "tslib": "^2.6.2" } }, "sha512-6sJut6/D21xPIK8EFMu+JJeF+fBCOmQKN1BRpeUYFi5m9P1CJpTYbBwfI107h7PHObI6a5bsckiKkRpF2orHpw=="], + + "@dnd-kit-svelte/utilities/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="], + + "@dnd-kit/abstract/@dnd-kit/state": ["@dnd-kit/state@0.2.4", "", { "dependencies": { "@preact/signals-core": "^1.10.0", "tslib": "^2.6.2" } }, "sha512-mPtQfmqBZBcVKa+Fi8AV2ocQWa+phvZa9Muq3PxFM3WIyjrFiEosGGPiPcx5hC3K+i2gkmr6Msc7iPrrpYkB5g=="], + + "@dnd-kit/collision/@dnd-kit/abstract": ["@dnd-kit/abstract@0.1.21", "", { "dependencies": { "@dnd-kit/geometry": "^0.1.21", "@dnd-kit/state": "^0.1.21", "tslib": "^2.6.2" } }, "sha512-6sJut6/D21xPIK8EFMu+JJeF+fBCOmQKN1BRpeUYFi5m9P1CJpTYbBwfI107h7PHObI6a5bsckiKkRpF2orHpw=="], + + "@dnd-kit/collision/@dnd-kit/geometry": ["@dnd-kit/geometry@0.1.21", "", { "dependencies": { "@dnd-kit/state": "^0.1.21", "tslib": "^2.6.2" } }, "sha512-Tir97wNJbopN2HgkD7AjAcoB3vvrVuUHvwdPALmNDUH0fWR637c4MKQ66YjjZAbUEAR8KL6mlDiHH4MzTLd7CQ=="], + + "@dnd-kit/dom/@dnd-kit/abstract": ["@dnd-kit/abstract@0.1.21", "", { "dependencies": { "@dnd-kit/geometry": "^0.1.21", "@dnd-kit/state": "^0.1.21", "tslib": "^2.6.2" } }, "sha512-6sJut6/D21xPIK8EFMu+JJeF+fBCOmQKN1BRpeUYFi5m9P1CJpTYbBwfI107h7PHObI6a5bsckiKkRpF2orHpw=="], + + "@dnd-kit/dom/@dnd-kit/geometry": ["@dnd-kit/geometry@0.1.21", "", { "dependencies": { "@dnd-kit/state": "^0.1.21", "tslib": "^2.6.2" } }, "sha512-Tir97wNJbopN2HgkD7AjAcoB3vvrVuUHvwdPALmNDUH0fWR637c4MKQ66YjjZAbUEAR8KL6mlDiHH4MzTLd7CQ=="], + + "@dnd-kit/geometry/@dnd-kit/state": ["@dnd-kit/state@0.2.4", "", { "dependencies": { "@preact/signals-core": "^1.10.0", "tslib": "^2.6.2" } }, "sha512-mPtQfmqBZBcVKa+Fi8AV2ocQWa+phvZa9Muq3PxFM3WIyjrFiEosGGPiPcx5hC3K+i2gkmr6Msc7iPrrpYkB5g=="], + + "@firebase/app/idb": ["idb@7.1.1", "", {}, "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="], + + "@firebase/installations/idb": ["idb@7.1.1", "", {}, "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="], + + "@firebase/messaging/idb": ["idb@7.1.1", "", {}, "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="], + + "@rollup/plugin-commonjs/is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + + "@testing-library/jest-dom/dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], + + "@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "@vitest/pretty-format/tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + + "@vitest/runner/@vitest/utils": ["@vitest/utils@4.1.5", "", { "dependencies": { "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug=="], + + "@vitest/snapshot/@vitest/utils": ["@vitest/utils@4.1.5", "", { "dependencies": { "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug=="], + + "@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + + "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "mode-watcher/runed": ["runed@0.25.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg=="], + + "mode-watcher/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="], + + "paneforge/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], + + "paneforge/svelte-toolbelt": ["svelte-toolbelt@0.9.3", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.29.0", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.30.2" } }, "sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw=="], + + "pretty-format/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "rollup/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "svelte/esrap": ["esrap@2.2.6", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-WN0clHt0a4mzC780UBVVBpsj4vSSjOFNRd2WjYtduB9HeKxm1sjHMNUwLEHVjI3FdCQD/Hurgz9ftbKEzP79Ow=="], + + "svelte-ast-print/esrap": ["esrap@1.2.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "@types/estree": "^1.0.1" } }, "sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw=="], + + "svelte-ast-print/zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="], + + "svelte-sonner/runed": ["runed@0.28.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ=="], + + "vaul-svelte/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], + + "vaul-svelte/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="], + + "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "vite-plugin-devtools-json/uuid": ["uuid@11.1.1", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ=="], + + "vitest/@vitest/expect": ["@vitest/expect@4.1.5", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.5", "@vitest/utils": "4.1.5", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw=="], + + "vitest/@vitest/mocker": ["@vitest/mocker@4.1.5", "", { "dependencies": { "@vitest/spy": "4.1.5", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw=="], + + "vitest/@vitest/spy": ["@vitest/spy@4.1.5", "", {}, "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ=="], + + "vitest/@vitest/utils": ["@vitest/utils@4.1.5", "", { "dependencies": { "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug=="], + + "vitest/tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "@dnd-kit-svelte/svelte/@dnd-kit/abstract/@dnd-kit/geometry": ["@dnd-kit/geometry@0.1.21", "", { "dependencies": { "@dnd-kit/state": "^0.1.21", "tslib": "^2.6.2" } }, "sha512-Tir97wNJbopN2HgkD7AjAcoB3vvrVuUHvwdPALmNDUH0fWR637c4MKQ66YjjZAbUEAR8KL6mlDiHH4MzTLd7CQ=="], + + "@dnd-kit-svelte/utilities/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], + + "@vitest/runner/@vitest/utils/tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + + "@vitest/snapshot/@vitest/utils/tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + + "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "mode-watcher/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], + + "paneforge/svelte-toolbelt/runed": ["runed@0.29.2", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA=="], + + "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], + + "vitest/@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + } +} diff --git a/bun.lockb b/bun.lockb deleted file mode 100755 index 52bad831ce0369ba833140d1d46118d17a57f553..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197391 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 A)&Kwi diff --git a/package.json b/package.json index e8cdbaa..eb20ce4 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "@yume-chan/scrcpy": "^2.3.0", "@yume-chan/stream-extra": "^2.5.3", "animejs": "^4.3.6", - "firebase": "^12.11.0", + "firebase": "^12.14.0", "idb": "^8.0.3", "mode-watcher": "^1.1.0", "usb": "^2.17.0", diff --git a/src/app.d.ts b/src/app.d.ts index da08e6d..ef3fdab 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -10,4 +10,9 @@ declare global { } } +declare module '*?raw' { + const content: string; + export default content; +} + export {}; diff --git a/src/lib/components/android-recipe-export-view.svelte b/src/lib/components/android-recipe-export-view.svelte new file mode 100644 index 0000000..435478c --- /dev/null +++ b/src/lib/components/android-recipe-export-view.svelte @@ -0,0 +1,618 @@ + + +
+
+
+
+

Android Recipe Export

+ + + {isAdbConnected ? 'ADB Connected' : 'ADB Offline'} + + {#if isAdbConnected} + + + {isAndroidSocketConnected ? 'Socket Connected' : 'ADB File Mode'} + + {/if} +
+

+ Receive exported recipe data from Machine. + {#if lastLoadedAt} + Last loaded {lastLoadedAt} + {/if} +

+
+ +
+
+ + +
+ {#if isAdbConnected} + + {/if} + {#if exportPayload} + + {/if} + {#if !isAdbConnected} + + {/if} +
+
+ + {#if error} +
+ {error} +
+ {/if} + + {#if parsed.headers.length > 0} +
+
+ {#if totalFilteredRows !== totalRows} + Showing {visibleRows.length} of {totalFilteredRows} matching rows. Page {currentPage} of + {totalPages}. + {:else} + Showing rows {pageStartIndex + 1}-{pageStartIndex + visibleRows.length} of {totalRows}. + Page {currentPage} of {totalPages}. + {/if} +
+ +
+
+ {versionLabel} +
+ + + {#if fieldRow} + + + {#each fieldRow.cells as cell, cellIndex} + + {/each} + + + {/if} + + {#each visibleRows as row} + + {#each row.cells as cell, cellIndex} + {@const emptyCell = isEmptyCell(cell)} + {@const ingredientCell = !emptyCell && isIngredientColumn(cellIndex)} + + {/each} + + {/each} + +
+ {cell || `Column ${cellIndex + 1}`} +
+ {#if cellIndex === 0} + + {:else} + {cell || '-'} + {/if} +
+
+ +
+

+ Page {currentPage} / {totalPages} +

+
+ + +
+
+
+ {:else if !isAdbConnected} +
+

Connect Android board first

+

+ Connect ADB to load the exported recipe file from Android. +

+ +
+ {:else if !isAndroidSocketConnected} +
+

Load exported recipe file

+

+ ADB is connected. Load the latest exported file from + {ANDROID_RECIPE_EXPORT_PATH}. +

+
+ +
+
+ {:else if parsingExport} +
+

Preparing recipe table

+

Recipe export received. Preparing the table pages.

+
+ {:else if parsed.headers.length === 0} +
+

Preparing recipe data

+

Please wait while the latest recipe list is loading.

+
+ {/if} +
+ + + + {@const selectedIngredients = getMenuIngredients(selectedMenuRow)} + + +
+
+ + + +
+
+

+ {selectedMenuRow?.cells[0] || 'Menu'} +

+

+ {#if selectedIngredients.length > 0} + {selectedIngredients.length} ingredient{selectedIngredients.length > 1 ? 's' : ''} used + {:else} + No ingredients + {/if} +

+
+
+ + + {#if selectedIngredients.length > 0} +
+ {#each selectedIngredients as ingredient, i} +
+
+ {i + 1} +
+
+

{ingredient.name}

+
+
+ + {ingredient.value} + +
+
+ {/each} +
+ {:else} +
+
+ + + +
+

No ingredients found

+

This menu has no ingredient values

+
+ {/if} + + +
+ +
+
+
diff --git a/src/lib/components/app-sidebar.svelte b/src/lib/components/app-sidebar.svelte index d9fa564..d7475dd 100644 --- a/src/lib/components/app-sidebar.svelte +++ b/src/lib/components/app-sidebar.svelte @@ -3,9 +3,7 @@ import { onDestroy, type ComponentProps } from 'svelte'; import { asset } from '$app/paths'; import AppAccountSelect from './app-account-select.svelte'; - import { needPermission } from '$lib/core/handlers/permissionHandler'; import { - Code, LayoutDashboard, LucideEye, CherryIcon, @@ -13,21 +11,43 @@ BugIcon, CupSodaIcon, Shield, - FileSpreadsheet + FileSpreadsheet, + MonitorSmartphone, + PlusCircle, + ImageUp, + Video, + Sun, + Moon } from '@lucide/svelte/icons'; import TaobinLogo from '$lib/assets/logo.svelte'; import { goto } from '$app/navigation'; import Button from '$lib/components/ui/button/button.svelte'; import { sidebarStore } from '$lib/core/stores/sidebar'; import { auth } from '$lib/core/stores/auth'; + import { permission as permissionStore } from '$lib/core/stores/permissions'; import { isUserAdmin } from '$lib/core/admin/adminService'; import { referenceFromPage } from '$lib/core/stores/recipeStore'; import { env } from '$env/dynamic/public'; + import { toggleMode, mode } from 'mode-watcher'; let sideBar: HTMLElement | null = $state(null); let isSideBarOpen: boolean = $state(true); let isAdmin: boolean = $state(false); + // Helper function to check permission (reactive version) + function checkPermission(requiredPerm: string, userPerms: string[]): boolean { + if (!requiredPerm) return true; + + const reqParts = requiredPerm.split('.'); + return userPerms.some((userPerm) => { + const userParts = userPerm.split('.'); + if (userParts.length !== reqParts.length) return false; + return reqParts.every( + (part, i) => part === '*' || userParts[i] === '*' || part === userParts[i] + ); + }); + } + const app_version = env.PUBLIC_APP_VERSION; const data = { @@ -75,11 +95,35 @@ icon: CupSodaIcon, requirePerm: '' }, + { + title: 'Create Menu', + url: '/tools/create-menu', + icon: PlusCircle, + requirePerm: '' + }, { title: 'Debug', url: '/tools/debug', icon: BugIcon, requirePerm: '' + }, + { + title: 'Android Recipes', + url: '/tools/android-recipe', + icon: MonitorSmartphone, + requirePerm: '' + }, + { + title: 'Image Upload', + url: '/tools/image-upload', + icon: ImageUp, + requirePerm: '' + }, + { + title: 'Adv Upload', + url: '/tools/adv-upload', + icon: Video, + requirePerm: '' } ] }, @@ -136,13 +180,13 @@ unsubSidebar(); }); + // Reactive: re-filter when $permissionStore changes let authorizedNavMain = $derived( data.navMain .map((nav) => { const filteredItems = nav.items.filter((item) => { if (!item.requirePerm) return true; - - return needPermission(item.requirePerm); + return checkPermission(item.requirePerm, $permissionStore); }); return { ...nav, items: filteredItems }; @@ -160,8 +204,8 @@
-

@@ -212,17 +256,7 @@ {#snippet child({ props })} - { - if (nav.title === 'Sheet') { - e.preventDefault(); - referenceFromPage.set('sheet'); - goto(sub.url); - } - }} - > + {#if sub.icon} {/if} @@ -238,6 +272,26 @@ {/if} +

diff --git a/src/lib/components/recipe-details/recipe-detail.svelte b/src/lib/components/recipe-details/recipe-detail.svelte index b1100ea..77673d0 100644 --- a/src/lib/components/recipe-details/recipe-detail.svelte +++ b/src/lib/components/recipe-details/recipe-detail.svelte @@ -111,7 +111,14 @@ async function getCurrentQueue() { let inst = adb.getAdbInstance(); if (inst) { - let current_brewing = await adb.pull(env.PUBLIC_BREW_CURRENT_RECIPE); + const currentRecipePath = env.PUBLIC_BREW_CURRENT_RECIPE; + if (!currentRecipePath) { + return { + error: 'PUBLIC_BREW_CURRENT_RECIPE is not configured' + }; + } + + let current_brewing = await adb.pull(currentRecipePath); // console.log(`current brewing queue: ${current_brewing}`); if (current_brewing === '') { current_brewing = '{}'; diff --git a/src/lib/components/recipe-editor-dialog.svelte b/src/lib/components/recipe-editor-dialog.svelte index c80a281..8ee583d 100644 --- a/src/lib/components/recipe-editor-dialog.svelte +++ b/src/lib/components/recipe-editor-dialog.svelte @@ -269,6 +269,21 @@ } } + async function saveRecipeMenuFileToAndroid() { + if (refPage != 'brew') return; + + const recipeMenu = ready_to_send_brew[0] ?? $state.snapshot(currentData); + if (!recipeMenu?.productCode) { + addNotification('ERR:Recipe data is not ready to save'); + return; + } + + const sent = await adb.sendRecipeMenuFileToAndroid(recipeMenu); + if (sent) { + addNotification(`INFO:Save recipe menu file request sent: ${recipeMenu.productCode}`); + } + } + function onCloseDialog() { currentEditingRecipeProductCode.set(''); callback_revert_value_if_not_save(save_change); @@ -373,6 +388,7 @@ save_change = true; callback_revert_value_if_not_save(save_change); + await saveRecipeMenuFileToAndroid(); addNotification('INFO:Save recipe'); }} diff --git a/src/lib/core/adb/adb.ts b/src/lib/core/adb/adb.ts index 6abe7ea..2cab7d5 100644 --- a/src/lib/core/adb/adb.ts +++ b/src/lib/core/adb/adb.ts @@ -14,9 +14,53 @@ import { handleAdbPayload } from '../handlers/adbPayloadHandler'; import { adbWriter } from '../stores/adbWriter'; import { WritableStream } from '@yume-chan/stream-extra'; import { env } from '$env/dynamic/public'; -import type Dice_2 from '@lucide/svelte/icons/dice-2'; +import { get } from 'svelte/store'; let syncConnection: any = null; +let syncOperation: Promise = Promise.resolve(); +let recipeMenuAdbConnectPromise: Promise | null = null; +let recipeMenuAndroidServerConnectPromise: Promise | null = null; +let recipeMenuAndroidServerRetryTimer: ReturnType | null = null; + +async function runSyncOperation(operation: () => Promise) { + const run = syncOperation.then(operation, operation); + syncOperation = run.catch(() => {}); + return await run; +} + +function clearRecipeMenuAndroidServerRetry() { + if (recipeMenuAndroidServerRetryTimer) { + clearTimeout(recipeMenuAndroidServerRetryTimer); + recipeMenuAndroidServerRetryTimer = null; + } +} + +function scheduleRecipeMenuAndroidServerReconnect(delayMs = 2000) { + if (recipeMenuAndroidServerRetryTimer || !getAdbInstance()) return; + + recipeMenuAndroidServerRetryTimer = setTimeout(() => { + recipeMenuAndroidServerRetryTimer = null; + void connectToAndroidRecipeMenuServer(false); + }, delayMs); +} + +async function connectRecipeMenuWebUsbDevice( + device: AdbDaemonWebUsbDevice, + credentialStore: AdbWebCredentialStore +) { + const connection = await device.connect(); + const transport = await AdbDaemonTransport.authenticate({ + connection: connection, + serial: device.serial, + credentialStore: credentialStore + }); + + const adb = new Adb(transport); + await saveAdbInstance(adb); + await connectToAndroidRecipeMenuServer(); + + return adb; +} function isRecoverableError(error: any): boolean { if (!error) return false; @@ -91,7 +135,7 @@ async function connectWithRetry( ); } -export async function connnectViaWebUSB() { +export async function connnectViaWebUSB(connectAndroidServer = true) { const device = await AdbDaemonWebUsbDeviceManager.BROWSER?.requestDevice(); console.log('usb ok', globalThis.navigator.usb); if (device) { @@ -109,7 +153,9 @@ export async function connnectViaWebUSB() { const adb = new Adb(transport); await saveAdbInstance(adb); - await connectToAndroidServer(); + if (connectAndroidServer) { + await connectToAndroidServer(); + } // save device info await deviceCredentialManager.saveDeviceInfo(device); @@ -129,7 +175,8 @@ export async function connnectViaWebUSB() { export async function connectDeviceByCred( device: AdbDaemonWebUsbDevice, - credStore: AdbWebCredentialStore + credStore: AdbWebCredentialStore, + connectAndroidServer = true ) { try { const connection = await device.connect(); @@ -142,7 +189,9 @@ export async function connectDeviceByCred( const adb = new Adb(transport); await saveAdbInstance(adb); - await connectToAndroidServer(); + if (connectAndroidServer) { + await connectToAndroidServer(); + } return true; } catch (error) { @@ -159,6 +208,112 @@ export function getAdbInstance() { return AdbInstance.instance; } +export async function connectRecipeMenuViaWebUSB() { + const currentInstance = getAdbInstance(); + if (currentInstance) { + await connectToAndroidRecipeMenuServer(); + return currentInstance; + } + if (recipeMenuAdbConnectPromise) return await recipeMenuAdbConnectPromise; + + const device = await AdbDaemonWebUsbDeviceManager.BROWSER?.requestDevice(); + console.log('recipe menu usb ok', 'usb' in globalThis.navigator); + if (device) { + console.log('recipe menu connect ', device.name); + + try { + const credentialStore = new AdbWebCredentialStore(); + recipeMenuAdbConnectPromise = connectRecipeMenuWebUsbDevice(device, credentialStore); + const adb = await recipeMenuAdbConnectPromise; + + await deviceCredentialManager.saveDeviceInfo(device); + return adb; + } catch (e: any) { + console.error('recipe menu connect error', e); + + if (e instanceof AdbDaemonWebUsbDevice.DeviceBusyError) { + addNotification( + 'ERR:Device is already in use by another program, please close the program and try again' + ); + } + + throw e; + } finally { + recipeMenuAdbConnectPromise = null; + } + } +} + +export async function connectRecipeMenuDeviceByCred( + device: AdbDaemonWebUsbDevice, + credStore: AdbWebCredentialStore +) { + const currentInstance = getAdbInstance(); + if (currentInstance) { + await connectToAndroidRecipeMenuServer(); + return true; + } + + if (recipeMenuAdbConnectPromise) { + await recipeMenuAdbConnectPromise; + return Boolean(getAdbInstance()); + } + + try { + recipeMenuAdbConnectPromise = connectRecipeMenuWebUsbDevice(device, credStore); + await recipeMenuAdbConnectPromise; + + return true; + } catch (error) { + throw error; + } finally { + recipeMenuAdbConnectPromise = null; + } +} + +export async function reconnectAndroidRecipeMenuServer() { + await connectToAndroidRecipeMenuServer(true); +} + +export async function sendRecipeMenuFileToAndroid(recipe: any) { + return await sendRecipeMenuMessageToAndroid({ + type: 'save_recipe_menu_file', + payload: { + time: new Date().toLocaleTimeString(), + data: recipe + } + }); +} + +export async function sendRecipeMenuMessageToAndroid(message: any) { + let writer: any = get(adbWriter); + + if (!writer) { + if (getAdbInstance()) { + await connectToAndroidRecipeMenuServer(false); + } else { + await connectRecipeMenuViaWebUSB(); + } + writer = get(adbWriter); + } + + if (!writer) { + addNotification('ERR:No active Android recipe connection'); + return false; + } + + try { + const encoder = new TextEncoder(); + await writer.write(encoder.encode(JSON.stringify(message) + '\n')); + console.log('recipe menu sent! ', JSON.stringify(message).length); + return true; + } catch (error) { + console.error('recipe menu write failed', error); + addNotification(`ERR:Failed to send recipe menu\n${error}`); + return false; + } +} + export async function executeCmd(command: string) { let instance = getAdbInstance(); @@ -232,64 +387,117 @@ export async function cleanupSync() { } export async function pull(filename: string, timeoutMs: number = 5000) { - let instance = getAdbInstance(); + return await runSyncOperation(async () => { + let instance = getAdbInstance(); - await cleanupSync(); - - try { - if (instance) { - let chunkList: Uint8Array[] = []; - const syncProm = instance.sync(); - const timeoutProm = new Promise((_, reject) => { - setTimeout(() => reject(new Error('sync timeout')), timeoutMs); - }); - - syncConnection = await Promise.race([syncProm, timeoutProm]); - const content = syncConnection.read(filename); - let result_string = ''; - - for await (const chunk of content) { - result_string += new TextDecoder().decode(chunk); - } - - return result_string; - } - } catch (pull_error: any) { - console.log('pulling error', pull_error); - } finally { await cleanupSync(); - } + + try { + if (instance) { + let chunkList: Uint8Array[] = []; + const syncProm = instance.sync(); + const timeoutProm = new Promise((_, reject) => { + setTimeout(() => reject(new Error('sync timeout')), timeoutMs); + }); + + syncConnection = await Promise.race([syncProm, timeoutProm]); + const content = syncConnection.read(filename); + let result_string = ''; + + for await (const chunk of content) { + result_string += new TextDecoder().decode(chunk); + } + + return result_string; + } + } catch (pull_error: any) { + console.log('pulling error', pull_error); + } finally { + await cleanupSync(); + } + }); } export async function push(path: string, obj: string) { - let instance = getAdbInstance(); - if (instance) { - let sync = await instance.sync(); - const encoder = new TextEncoder(); + return await runSyncOperation(async () => { + let instance = getAdbInstance(); + if (instance) { + let sync = await instance.sync(); + const encoder = new TextEncoder(); + const file: ReadableStream> = new ReadableStream({ + start(controller) { + controller.enqueue(new Uint8Array(encoder.encode(obj))); + controller.close(); + } + }); + + try { + console.log('support push v2', sync.supportsSendReceiveV2); + + await sync.write({ + filename: path, + file + }); + } catch (error) { + console.log('error while trying to write to machine', error); + } finally { + await sync.dispose(); + } + } + }); +} + +// Push a binary file (e.g. an .mp4 video) to the machine. Unlike push() which +// text-encodes a string, this streams raw bytes in chunks so binary data is not +// corrupted, and reports progress. +export async function pushBinary( + path: string, + data: Uint8Array, + onProgress?: (sent: number, total: number) => void +): Promise { + return await runSyncOperation(async () => { + let instance = getAdbInstance(); + if (!instance) return false; + + const total = data.byteLength; + onProgress?.(0, total); + + let sync = await instance.sync(); + + // Mirror the working text push(): a single enqueue then close. @yume-chan + // packetizes internally for the ADB protocol; a multi-chunk or pull-based + // stream can stall the transfer. const file: ReadableStream> = new ReadableStream({ start(controller) { - controller.enqueue(new Uint8Array(encoder.encode(obj))); + controller.enqueue(data); controller.close(); } }); try { - console.log('support push v2', sync.supportsSendReceiveV2); - - await sync.write({ - filename: path, - file - }); + const writeProm = sync.write({ filename: path, file }); + // Safety net so a stalled transfer can't hang the UI forever. + const timeoutProm = new Promise((_, reject) => + setTimeout(() => reject(new Error('push write timeout (120s)')), 120000) + ); + await Promise.race([writeProm, timeoutProm]); + onProgress?.(total, total); + return true; } catch (error) { - console.log('error while trying to write to machine', error); + console.log('error while pushing binary to machine', error); + return false; } finally { await sync.dispose(); } - } + }); } // NOTE: adb reverse is not work by unavailable features support +export async function reconnectAndroidServer() { + await connectToAndroidServer(); +} + async function connectToAndroidServer(maxRetries = 5) { let lastError: any; for (let attempt = 0; attempt < maxRetries; attempt++) { @@ -300,10 +508,12 @@ async function connectToAndroidServer(maxRetries = 5) { return; } + const brewConnectionPort = env.PUBLIC_BREW_CONN_PORT || 'tcp:36588'; + // add retry mechanism const stream = await connectWithRetry( - async () => inst.transport.connect(env.PUBLIC_BREW_CONN_PORT), - `connect to Android server port ${env.PUBLIC_BREW_CONN_PORT}`, + async () => inst.transport.connect(brewConnectionPort), + `connect to Android server port ${brewConnectionPort}`, 3, 500 ); @@ -316,22 +526,24 @@ async function connectToAndroidServer(maxRetries = 5) { if (writer) { addNotification('INFO:Enable Brewing Mode T on machine'); - try { - while (true) { - const { value, done } = await reader.read(); - if (done) break; - handleAdbPayload(new TextDecoder().decode(value)); + (async () => { + try { + while (true) { + const { value, done } = await reader.read(); + if (done) break; + handleAdbPayload(new TextDecoder().decode(value)); + } + } catch (e) { + console.error('read error', e); + if (isRecoverableError(e)) { + void connectToAndroidServer(); + } + } finally { + adbWriter.set(null); + addNotification('WARN:Brewing Mode T Offline ...'); } - } catch (e) { - console.error('read error', e); - if (isRecoverableError(e)) { - throw e; - } - throw e; - } finally { - adbWriter.set(null); - addNotification('WARN:Brewing Mode T Offline ...'); - } + })(); + return; } else { addNotification('WARN:Brewing Mode T unavailable'); @@ -366,6 +578,94 @@ async function connectToAndroidServer(maxRetries = 5) { } } +async function connectToAndroidRecipeMenuServer(notifyFailure = true, retryOnFailure = false) { + if (recipeMenuAndroidServerConnectPromise) return recipeMenuAndroidServerConnectPromise; + + recipeMenuAndroidServerConnectPromise = connectToAndroidRecipeMenuServerOnce( + notifyFailure, + retryOnFailure + ).finally(() => { + recipeMenuAndroidServerConnectPromise = null; + }); + + return recipeMenuAndroidServerConnectPromise; +} + +async function connectToAndroidRecipeMenuServerOnce(notifyFailure = true, retryOnFailure = false) { + try { + let inst = getAdbInstance(); + if (!inst) { + console.warn('recipe menu adb instance not found'); + return; + } + + const brewConnectionPort = env.PUBLIC_BREW_CONN_PORT || 'tcp:36588'; + + clearRecipeMenuAndroidServerRetry(); + const stream = await inst.transport.connect(brewConnectionPort); + const writer = stream.writable.getWriter(); + const reader = stream.readable.getReader(); + + console.log('checking recipe menu writer ', writer); + adbWriter.set(writer); + if (writer) { + addNotification('INFO:Enable Android recipe menu channel'); + } else { + addNotification('WARN:Android recipe menu channel unavailable'); + + setTimeout(async () => { + console.log('reconnecting android recipe menu server'); + await connectToAndroidRecipeMenuServer(); + }, 5000); + } + + (async () => { + try { + let messageBuffer = ''; + const decoder = new TextDecoder(); + while (true) { + const { value, done } = await reader.read(); + if (done) break; + + const decoded = decoder.decode(value, { stream: true }); + console.log('[ADB Reader] Received raw:', decoded.slice(0, 200)); + messageBuffer += decoded; + const messages = messageBuffer.split('\n'); + messageBuffer = messages.pop() ?? ''; + + for (const message of messages) { + const trimmedMessage = message.trim(); + if (trimmedMessage) { + console.log('[ADB Reader] Processing message:', trimmedMessage.slice(0, 200)); + handleAdbPayload(trimmedMessage); + } + } + } + + const remainingMessage = messageBuffer.trim(); + if (remainingMessage) { + handleAdbPayload(remainingMessage); + } + } catch (e) { + console.error('recipe menu read error', e); + } finally { + adbWriter.set(null); + addNotification('WARN:Android recipe menu channel offline ...'); + if (retryOnFailure) { + scheduleRecipeMenuAndroidServerReconnect(); + } + } + })(); + } catch (err) { + console.error('Recipe menu connection failed. Suspect java running or not', err); + adbWriter.set(null); + if (notifyFailure) addNotification('ERR:Fail to enable Android recipe menu channel'); + if (retryOnFailure) { + scheduleRecipeMenuAndroidServerReconnect(); + } + } +} + // logcat stream // TODO: screen mirror diff --git a/src/lib/core/auth/domainBlocker.ts b/src/lib/core/auth/domainBlocker.ts index 3558bdc..f703f48 100644 --- a/src/lib/core/auth/domainBlocker.ts +++ b/src/lib/core/auth/domainBlocker.ts @@ -7,7 +7,6 @@ export async function checkAllowAccess(userDomain: string): Promise { if (snapshot.exists()) { let domains = snapshot.data(); - // console.log(`domains: ${JSON.stringify(domains)}`); return domains['account_email'].includes(userDomain); } diff --git a/src/lib/core/brew/command.ts b/src/lib/core/brew/command.ts index 9d44f81..80b2d51 100644 --- a/src/lib/core/brew/command.ts +++ b/src/lib/core/brew/command.ts @@ -18,8 +18,11 @@ async function sendCommand(type: string, params?: string[]) { let inst = adb.getAdbInstance(); if (inst) { try { + const commandPath = env.PUBLIC_BREW_CMD_WEB; + if (!commandPath) throw new BrewCommandError('PUBLIC_BREW_CMD_WEB is not configured'); + let cmd = type + ' ' + (params?.join(' ') ?? ''); - await adb.push(env.PUBLIC_BREW_CMD_WEB, cmd); + await adb.push(commandPath, cmd); } catch (e) { throw new BrewCommandError('Command failed', `${e}`); } @@ -32,9 +35,18 @@ async function sendReset() { let inst = adb.getAdbInstance(); if (inst) { try { - await adb.push(env.PUBLIC_BREW_CMD_WEB, ''); - await adb.push(env.PUBLIC_BREW_CURRENT_RECIPE, ''); - await adb.push(env.PUBLIC_BREW_WEB_STATUS, ''); + const commandPath = env.PUBLIC_BREW_CMD_WEB; + const currentRecipePath = env.PUBLIC_BREW_CURRENT_RECIPE; + const statusPath = env.PUBLIC_BREW_WEB_STATUS; + + if (!commandPath) throw new BrewCommandError('PUBLIC_BREW_CMD_WEB is not configured'); + if (!currentRecipePath) + throw new BrewCommandError('PUBLIC_BREW_CURRENT_RECIPE is not configured'); + if (!statusPath) throw new BrewCommandError('PUBLIC_BREW_WEB_STATUS is not configured'); + + await adb.push(commandPath, ''); + await adb.push(currentRecipePath, ''); + await adb.push(statusPath, ''); } catch (e) { throw new BrewCommandError('Reset failed', `${e}`); } diff --git a/src/lib/core/handlers/adbPayloadHandler.ts b/src/lib/core/handlers/adbPayloadHandler.ts index 63cb669..8d6f63d 100644 --- a/src/lib/core/handlers/adbPayloadHandler.ts +++ b/src/lib/core/handlers/adbPayloadHandler.ts @@ -1,42 +1,73 @@ import { updateMachineStatus } from '../stores/machineInfoStore'; import { addNotification } from '../stores/noti'; +import { + loadAndroidRecipeExportFromDevice, + saveAndroidRecipeExportPayload +} from '../services/androidRecipeExportService'; import { handleIncomingMessages } from './messageHandler'; +import { setMenuSaved, setMenuSaveError } from '../stores/menuSaveStore'; type AdbPayload = { type: string; payload: any }; async function handleAdbPayload(raw_payload: string) { - console.log('get payload', raw_payload); + console.log('[ADB] Received payload:', raw_payload.slice(0, 300)); try { const payload: AdbPayload = JSON.parse(raw_payload); + console.log('[ADB] Parsed type:', payload.type, 'payload:', payload.payload); switch (payload.type) { case 'log': let log_level = payload.payload['level'] ?? 'INFO'; let log_message = payload.payload['msg'] ?? ''; - if (log_message !== '') addNotification(`${log_level}`); + if (log_message !== '') { + console.log('[ADB LOG]', log_level, log_message); + addNotification(`${log_level}:${log_message}`); + } break; case 'response': - if (payload.payload instanceof String) { + if (typeof payload.payload === 'string' || payload.payload instanceof String) { // single message response let raw_payload = payload.payload.toString(); - if (raw_payload.startsWith('save_recipe_machine')) { + if ( + raw_payload.startsWith('save_recipe_machine') || + raw_payload.startsWith('save_recipe_menu_file') + ) { let res = raw_payload.split('/'); let pd = res[1] ?? ''; let action = res[2] ?? ''; let uiAction = res[3] ?? ''; - handleIncomingMessages( - JSON.stringify({ - type: 'ui_action', - payload: { - action: uiAction, - from: 'brew', - ref: `${pd}.${action}` - } - }) - ); + console.log('[ADB] Save response parsed:', { pd, action, uiAction, raw_payload }); + + // Track menu save status + if (raw_payload.startsWith('save_recipe_menu_file') && pd) { + if (action === 'success' || action === 'ok' || uiAction === 'refreshNow') { + setMenuSaved(pd); + addNotification(`INFO:Menu saved: ${pd}`); + } else if (action === 'error' || action === 'fail') { + setMenuSaveError(pd, 'Save failed'); + addNotification(`ERR:Failed to save menu: ${pd}`); + } else { + // Assume success if we get a response + setMenuSaved(pd); + addNotification(`INFO:Menu saved: ${pd}`); + } + } + + if (raw_payload.startsWith('save_recipe_machine')) { + handleIncomingMessages( + JSON.stringify({ + type: 'ui_action', + payload: { + action: uiAction, + from: 'brew', + ref: `${pd}.${action}` + } + }) + ); + } } else if (raw_payload.startsWith('state')) { let res = raw_payload.split('/'); let new_machine_state = res[1] ?? ''; @@ -84,6 +115,39 @@ async function handleAdbPayload(raw_payload: string) { addNotification(`ERR:${payload.payload}`); // send message to server if needed break; + case 'recipe-export': + if (payload.payload?.content) { + saveAndroidRecipeExportPayload({ + content: payload.payload.content, + exportedAt: payload.payload.exportedAt, + source: payload.payload.source, + fileSizeBytes: payload.payload.fileSizeBytes, + lineCount: payload.payload.lineCount, + message: payload.payload.message + }); + addNotification('INFO:Recipe export received from Android'); + } else if (payload.payload?.message) { + addNotification(`ERR:${payload.payload.message}`); + } + break; + case 'recipe-export-ready': + if (payload.payload?.message && payload.payload?.fileSizeBytes === 0) { + addNotification(`ERR:${payload.payload.message}`); + break; + } + + void loadAndroidRecipeExportFromDevice({ + exportedAt: payload.payload?.exportedAt, + source: payload.payload?.source, + fileSizeBytes: payload.payload?.fileSizeBytes, + lineCount: payload.payload?.lineCount, + message: payload.payload?.message + }) + .then(() => addNotification('INFO:Recipe export loaded from Android')) + .catch((error: any) => + addNotification(`ERR:${error?.message ?? 'Unable to load recipe export from Android'}`) + ); + break; default: } } catch (error: any) { diff --git a/src/lib/core/handlers/messageHandler.ts b/src/lib/core/handlers/messageHandler.ts index c7e2088..1f8c69b 100644 --- a/src/lib/core/handlers/messageHandler.ts +++ b/src/lib/core/handlers/messageHandler.ts @@ -12,6 +12,24 @@ import { toppingGroupFromServerQuery, toppingListFromServerQuery } from '../stores/recipeStore'; +import { + handleSheetStreamStart, + handleSheetStreamChunk, + handleSheetStreamEnd, + handleSheetStreamError, + handleCatalogsResponse, + handleListMenuResponse, + sheetCatalogsLoading, + handleRawStreamHeader, + handleRawStreamChunk, + handleRawStreamEnd +} from '../stores/sheetStore'; +import { + handleGenLayoutBatchStart, + handleGenLayoutFile, + handleGenLayoutBatchEnd, + handleGenLayoutError +} from '../stores/genLayoutStore'; import { buildOverviewFromServer } from '$lib/data/recipeService'; import { auth } from '../client/firebase'; import { type RecipeVersion } from '$lib/models/recipe_version.model'; @@ -202,19 +220,105 @@ const handlers: Record void> = { }, stream_patch_update: (p) => {}, notify: (p) => { - let noti_level = p.level ?? 'INFO'; - let msg = p.msg ?? `Notify from ${p.from}`; - let target = p.to; + const from = p.from; + const level = p.level ?? 'INFO'; + const msg = p.msg; + const target = p.to; + // Handle list-menu response + if (from === 'list-menu') { + const currentUid = auth.currentUser?.uid; + if (target && currentUid && target === currentUid && p.value) { + handleListMenuResponse({ codes: p.value }); + } + return; + } + + // Handle gen-service responses + if (from === 'gen-service') { + switch (level) { + case 'batch_start': + handleGenLayoutBatchStart({ + batch_id: p.batch_id, + total_files: p.total_files, + total_size_bytes: p.total_size_bytes + }); + addNotification(`INFO:Gen Layout started (${p.total_files} files)`); + break; + case 'file': + handleGenLayoutFile({ + batch_id: p.batch_id, + file_index: p.file_index, + total_files: p.total_files, + file: p.file, + content: p.content, + is_chunked: p.is_chunked, + part_index: p.part_index, + total_parts: p.total_parts, + is_last_part: p.is_last_part + }); + break; + case 'batch_end': + handleGenLayoutBatchEnd({ + batch_id: p.batch_id, + total_files: p.total_files + }); + addNotification('INFO:Gen Layout complete'); + break; + case 'ERROR': + handleGenLayoutError(msg); + addNotification(`ERR:Gen Layout error: ${msg}`); + break; + default: + console.log('[GenService] Received:', level, msg); + } + return; + } + + if (from === 'sheet-service' && level === 'content') { + const currentUid = auth.currentUser?.uid; + + if (target && currentUid && target === currentUid) { + if (!msg && p.content?.catalogs) { + handleCatalogsResponse(p.content); + addNotification(`INFO:Loaded ${p.content.catalogs?.length || 0} catalogs`); + return; + } + + // Handle streaming messages (with msg field) + switch (msg) { + case 'start': + handleSheetStreamStart(p); + addNotification('INFO:Sheet data streaming started'); + break; + case 'chunk': + handleSheetStreamChunk(p); + break; + case 'end': + handleSheetStreamEnd(p); + addNotification('INFO:Sheet data streaming complete'); + break; + case 'error': + handleSheetStreamError(p); + addNotification(`ERR:Sheet streaming error: ${p.content?.error_detail}`); + break; + default: + // Handle other content notifications from sheet-service + console.log('[Sheet] Received content:', p.content); + } + } + return; + } + + // Default notification handling if (target) { - // let currentUsername = auth.currentUser?.displayName; if (currentUsername && currentUsername === target) { - addNotification(`${noti_level}:${msg}`); + addNotification(`${level}:${msg}`); } } else { // broadcast to all - addNotification(`${noti_level}:${msg}`); + addNotification(`${level}:${msg}`); } }, ui_action: (p) => { @@ -259,12 +363,33 @@ const handlers: Record void> = { socketConnectionOfflineCount.set(0); socketAlreadySendHeartbeat.set(0); console.log('heartbeat reset offline count'); + }, + // Raw stream handlers for sheet data (e.g., price) + raw_stream: (p) => { + // Format: raw_stream with subtype in payload + // Header: { subtype: 'price', request_id, header?, country? } + const subtype = p.subtype; + if (subtype) { + handleRawStreamHeader(subtype, p); + } + }, + raw_stream_price: (p) => { + // Header for price stream + handleRawStreamHeader('price', p); + }, + raw_stream_chunk_price: (p) => { + // Chunk for price stream + handleRawStreamChunk('price', p); + }, + raw_stream_end_price: (p) => { + // End for price stream + handleRawStreamEnd('price', p); } }; export function handleIncomingMessages(raw: string) { const msg: WSMessage = JSON.parse(raw); - // console.log(`${new Date().toLocaleTimeString()}:ws msg`, msg); + // console.log(`[WS MSG] type=${msg.type}`, msg.payload); if (msg == null) { // error response addNotification('ERR:No response from server'); diff --git a/src/lib/core/handlers/ws_messageSender.ts b/src/lib/core/handlers/ws_messageSender.ts index 0ba1fdf..5d3dd01 100644 --- a/src/lib/core/handlers/ws_messageSender.ts +++ b/src/lib/core/handlers/ws_messageSender.ts @@ -18,7 +18,7 @@ function getServiceName(cmdReq: CommandRequest) { } // Websocket message wrapper for commands like `sheet`, `command` -export function sendCommandRequest(target: CommandRequest, values: any) { +export function sendCommandRequest(target: CommandRequest, values: any): boolean { let srv_name = getServiceName(target); let curr_user = get(auth); @@ -31,7 +31,7 @@ export function sendCommandRequest(target: CommandRequest, values: any) { }; } - sendMessage({ + return sendMessage({ type: target, payload: { user_info: user_info ?? {}, diff --git a/src/lib/core/services/androidRecipeExportService.ts b/src/lib/core/services/androidRecipeExportService.ts new file mode 100644 index 0000000..66c91ed --- /dev/null +++ b/src/lib/core/services/androidRecipeExportService.ts @@ -0,0 +1,284 @@ +import { browser } from '$app/environment'; +import { writable } from 'svelte/store'; + +export const ANDROID_RECIPE_EXPORT_PATH = '/mnt/sdcard/recipe_export_all.tsv'; +const ANDROID_RECIPE_EXPORT_CACHE_KEY = 'android_recipe_export_payload_v1'; +const ANDROID_RECIPE_EXPORT_DB_NAME = 'android_recipe_export_cache'; +const ANDROID_RECIPE_EXPORT_STORE_NAME = 'payloads'; + +export type AndroidRecipeExportPayload = { + content: string; + exportedAt?: number; + source?: string; + fileSizeBytes?: number; + lineCount?: number; + message?: string; +}; + +export type AndroidRecipeExportRow = { + lineNumber: number; + cells: string[]; + values: Record; +}; + +export type AndroidRecipeExportData = { + headers: string[]; + rows: AndroidRecipeExportRow[]; + lineCount: number; +}; + +export const androidRecipeExportPayload = writable(null); +let deviceExportLoadPromise: Promise | null = null; + +function openAndroidRecipeExportDb(): Promise { + return new Promise((resolve, reject) => { + const request = indexedDB.open(ANDROID_RECIPE_EXPORT_DB_NAME, 1); + + request.onupgradeneeded = () => { + request.result.createObjectStore(ANDROID_RECIPE_EXPORT_STORE_NAME); + }; + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); +} + +async function readCachedPayloadFromIndexedDb(): Promise { + const db = await openAndroidRecipeExportDb(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction(ANDROID_RECIPE_EXPORT_STORE_NAME, 'readonly'); + const store = transaction.objectStore(ANDROID_RECIPE_EXPORT_STORE_NAME); + const request = store.get(ANDROID_RECIPE_EXPORT_CACHE_KEY); + + request.onsuccess = () => resolve((request.result as AndroidRecipeExportPayload) ?? null); + request.onerror = () => reject(request.error); + transaction.oncomplete = () => db.close(); + transaction.onerror = () => { + db.close(); + reject(transaction.error); + }; + }); +} + +async function writeCachedPayloadToIndexedDb(payload: AndroidRecipeExportPayload): Promise { + const db = await openAndroidRecipeExportDb(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction(ANDROID_RECIPE_EXPORT_STORE_NAME, 'readwrite'); + const store = transaction.objectStore(ANDROID_RECIPE_EXPORT_STORE_NAME); + + store.put(payload, ANDROID_RECIPE_EXPORT_CACHE_KEY); + transaction.oncomplete = () => { + db.close(); + resolve(); + }; + transaction.onerror = () => { + db.close(); + reject(transaction.error); + }; + }); +} + +async function deleteCachedPayloadFromIndexedDb(): Promise { + const db = await openAndroidRecipeExportDb(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction(ANDROID_RECIPE_EXPORT_STORE_NAME, 'readwrite'); + const store = transaction.objectStore(ANDROID_RECIPE_EXPORT_STORE_NAME); + + store.delete(ANDROID_RECIPE_EXPORT_CACHE_KEY); + transaction.oncomplete = () => { + db.close(); + resolve(); + }; + transaction.onerror = () => { + db.close(); + reject(transaction.error); + }; + }); +} + +export async function loadCachedAndroidRecipeExport(): Promise { + if (!browser) return null; + + try { + const payload = await readCachedPayloadFromIndexedDb(); + if (!payload?.content) return null; + + androidRecipeExportPayload.set(payload); + return payload; + } catch (error) { + console.error('failed to load cached android recipe export from IndexedDB', error); + } + + try { + const cached = localStorage.getItem(ANDROID_RECIPE_EXPORT_CACHE_KEY); + if (!cached) return null; + + const payload = JSON.parse(cached) as AndroidRecipeExportPayload; + if (!payload?.content) return null; + + androidRecipeExportPayload.set(payload); + void writeCachedPayloadToIndexedDb(payload); + localStorage.removeItem(ANDROID_RECIPE_EXPORT_CACHE_KEY); + return payload; + } catch (error) { + console.error('failed to load legacy android recipe export cache', error); + return null; + } +} + +export function saveAndroidRecipeExportPayload(payload: AndroidRecipeExportPayload) { + androidRecipeExportPayload.set(payload); + + if (!browser) return; + + void writeCachedPayloadToIndexedDb(payload).catch((error) => { + console.error('failed to cache android recipe export', error); + }); +} + +export function clearCachedAndroidRecipeExport() { + androidRecipeExportPayload.set(null); + + if (!browser) return; + + localStorage.removeItem(ANDROID_RECIPE_EXPORT_CACHE_KEY); + void deleteCachedPayloadFromIndexedDb().catch((error) => { + console.error('failed to clear android recipe export cache', error); + }); +} + +function normalizeHeader(value: string, index: number): string { + const header = value.trim().replace(/^\uFEFF/, ''); + return header || `Column ${index + 1}`; +} + +function splitTsvLine(line: string): string[] { + return line.split('\t').map((cell) => cell.trim()); +} + +function collectNonEmptyLines(raw: string, maxLines: number): string[] { + const lines: string[] = []; + let lineStart = 0; + + for (let index = 0; index <= raw.length; index += 1) { + const isEnd = index === raw.length; + const char = raw[index]; + + if (!isEnd && char !== '\n') continue; + + const lineEnd = index > lineStart && raw[index - 1] === '\r' ? index - 1 : index; + const line = raw.slice(lineStart, lineEnd); + + if (line.trim().length > 0) { + lines.push(line); + if (lines.length >= maxLines) break; + } + + lineStart = index + 1; + } + + return lines; +} + +function buildUniqueHeaders(rawHeaders: string[], maxColumns: number): string[] { + const headers = [...rawHeaders]; + + for (let i = headers.length; i < maxColumns; i += 1) { + headers.push(`Column ${i + 1}`); + } + + const seen = new Map(); + return headers.map((header, index) => { + const normalized = normalizeHeader(header, index); + const count = seen.get(normalized) ?? 0; + seen.set(normalized, count + 1); + + return count === 0 ? normalized : `${normalized} ${count + 1}`; + }); +} + +export function parseAndroidRecipeExport( + raw: string, + maxRows = Number.POSITIVE_INFINITY +): AndroidRecipeExportData { + const maxLines = Number.isFinite(maxRows) ? Math.max(1, maxRows + 1) : Number.MAX_SAFE_INTEGER; + const lines = collectNonEmptyLines(raw, maxLines); + + if (lines.length === 0) { + return { + headers: [], + rows: [], + lineCount: 0 + }; + } + + const parsedLines = lines.map(splitTsvLine); + const maxColumns = Math.max(...parsedLines.map((line) => line.length)); + const headers = buildUniqueHeaders(parsedLines[0], maxColumns); + + const rows = parsedLines.slice(1).map((cells, index) => { + const paddedCells = [...cells]; + + for (let cellIndex = paddedCells.length; cellIndex < headers.length; cellIndex += 1) { + paddedCells.push(''); + } + + const values = Object.fromEntries( + headers.map((header, cellIndex) => [header, paddedCells[cellIndex] ?? '']) + ); + + return { + lineNumber: index + 2, + cells: paddedCells, + values + }; + }); + + return { + headers, + rows, + lineCount: lines.length + }; +} + +export async function pullAndroidRecipeExport(timeoutMs = 15000): Promise { + const adb = await import('$lib/core/adb/adb'); + const instance = adb.getAdbInstance(); + + if (!instance) { + throw new Error('ADB device is not connected'); + } + + const content = await adb.pull(ANDROID_RECIPE_EXPORT_PATH, timeoutMs); + + if (content === undefined) { + throw new Error(`Unable to pull ${ANDROID_RECIPE_EXPORT_PATH}`); + } + + return content; +} + +export async function loadAndroidRecipeExportFromDevice( + meta: Partial = {} +): Promise { + if (deviceExportLoadPromise) return deviceExportLoadPromise; + + deviceExportLoadPromise = (async () => { + const content = await pullAndroidRecipeExport(30000); + + saveAndroidRecipeExportPayload({ + content, + exportedAt: meta.exportedAt ?? Date.now(), + source: meta.source ?? ANDROID_RECIPE_EXPORT_PATH, + fileSizeBytes: meta.fileSizeBytes, + lineCount: meta.lineCount, + message: meta.message + }); + })().finally(() => { + deviceExportLoadPromise = null; + }); + + return deviceExportLoadPromise; +} diff --git a/src/lib/core/services/sheetService.ts b/src/lib/core/services/sheetService.ts new file mode 100644 index 0000000..2e15f35 --- /dev/null +++ b/src/lib/core/services/sheetService.ts @@ -0,0 +1,251 @@ +import { sendCommandRequest, sendMessage } from '../handlers/ws_messageSender'; +import { get } from 'svelte/store'; +import { auth } from '../stores/auth'; +import { + productCodesLoading, + hasSheetPriceBeenSent, + markSheetPriceAsSent, + sheetPriceLoading, + streamingRawData, + setPendingProductCodesCountry +} from '../stores/sheetStore'; +import { setGenLayoutGenerating } from '../stores/genLayoutStore'; + +export function requestCatalogs(country: string): boolean { + return sendCommandRequest('sheet', { + country: country, + param: 'catalogs' + }); +} + +export function enterRoom(country: string, catalog: string): boolean { + return sendCommandRequest('sheet', { + country: country, + catalog: catalog, + param: 'enter' + }); +} + +export function sendHeartbeat(country: string, catalog: string): boolean { + return sendCommandRequest('sheet', { + country: country, + catalog: catalog, + param: 'heartbeat' + }); +} + +export function exitRoom(country: string, catalog: string): boolean { + return sendCommandRequest('sheet', { + country: country, + catalog: catalog, + param: 'exit' + }); +} + +export function requestCatalogMenu(country: string, catalog: string): boolean { + return sendCommandRequest('sheet', { + country: country, + catalog: catalog, + param: 'catalog/menu' + }); +} + +export function updateMenu(country: string, catalog: string, content: any[]): boolean { + return sendCommandRequest('sheet', { + country: country, + catalog: catalog, + content: content, + param: 'update/menu' + }); +} + +export function addMenu(country: string, catalog: string, content: any[]): boolean { + console.log('[sheetService] Adding menu:', { country, catalog, content }); + const sent = sendCommandRequest('sheet', { + country: country, + catalog: catalog, + content: content, + param: 'add/menu' + }); + console.log('[sheetService] Add menu sent:', sent); + return sent; +} + +export function deleteMenu(country: string, catalog: string, targetIds: number[]): boolean { + const content = targetIds.map((id) => ({ target_id: id })); + return sendCommandRequest('sheet', { + country: country, + catalog: catalog, + content: content, + param: 'delete/menu' + }); +} + +export function swapMenu( + country: string, + catalog: string, + swaps: { source_id: number; target_id: number }[] +): boolean { + return sendCommandRequest('sheet', { + country: country, + catalog: catalog, + content: swaps, + param: 'swap/menu' + }); +} + +export function requestListMenu(country: string, boxid?: string): boolean { + const curr_user = get(auth); + + let user_info: any = {}; + if (curr_user) { + user_info = { + displayName: curr_user.displayName, + email: curr_user.email, + uid: curr_user.uid + }; + } + + productCodesLoading.set(true); + setPendingProductCodesCountry(country); + + console.log('[sheetService] Sending list_menu request for country:', country, 'boxid:', boxid); + + return sendMessage({ + type: 'list_menu', + payload: { + user_info, + country, + boxid: boxid || undefined + } + }); +} + +export function requestGenLayout(country: string): boolean { + const curr_user = get(auth); + + let user_info: any = {}; + if (curr_user) { + user_info = { + displayName: curr_user.displayName, + email: curr_user.email, + uid: curr_user.uid + }; + } + + setGenLayoutGenerating(); + + console.log('[sheetService] Sending gen-layout request for country:', country); + + return sendMessage({ + type: 'command', + payload: { + user_info, + srv_name: 'gen-service', + values: { + file_layout: 'sheet', + file_desc: 'sheet', + country: country, + param: 'new-inter-v3-multi-promotion-other_catalog-supra_app' + } + } + }); +} + +/** + * Request price data from sheet for specific product codes + * NOTE: Can only send once per type (price). Use hasSheetPriceBeenSent to check. + */ +export function requestSheetPrice(country: string, productCodes: string[]): boolean { + // Check if already sent + if (hasSheetPriceBeenSent('price')) { + console.warn('[sheetService] Price request already sent, skipping'); + return false; + } + + if (!productCodes || productCodes.length === 0) { + console.warn('[sheetService] No product codes to request price for'); + return false; + } + + // Generate request_id (UUID v4) + const request_id = crypto.randomUUID(); + + // Store request_id and country in streamingRawData for tracking + streamingRawData.update((data) => ({ + ...data, + price: { + request_id, + country, + chunks: [], + rawParts: [] + } + })); + + sheetPriceLoading.set(true); + + // Convert to array of objects (backend expects objects, not strings) + const content = productCodes.map((code) => ({ product_code: code })); + + console.log('[sheetService] Sending sheet price request for country:', country, 'codes:', productCodes.length, 'request_id:', request_id); + + const sent = sendCommandRequest('sheet', { + country: country, + content: content, + param: 'price', + stream: true, + request_id + }); + + if (sent) { + markSheetPriceAsSent('price'); + } else { + sheetPriceLoading.set(false); + } + + return sent; +} + +/** + * Update price data in sheet + * content: [{ row_index: number, cells: [{ value: string, coord: { row: number, col: number } }] }] + */ +export function updateSheetPrice( + country: string, + content: { row_index: number; cells: { value: string; coord: { row: number; col: number } }[] }[] +): boolean { + if (!content || content.length === 0) { + console.warn('[sheetService] No content to update'); + return false; + } + + console.log('[sheetService] Updating sheet price for country:', country, 'items:', content.length); + + return sendCommandRequest('sheet', { + country: country, + content: content, + param: 'update/price' + }); +} + +/** + * Add new price rows to sheet (for product codes that don't exist in price sheet) + * content: [{ cells: [product_code, name_en, name_th, ..., price, ...] }] + */ +export function addSheetPrice( + country: string, + content: { cells: string[] }[] +): boolean { + if (!content || content.length === 0) { + console.warn('[sheetService] No content to add'); + return false; + } + + console.log('[sheetService] Adding price rows for country:', country, 'items:', content.length, content); + + return sendCommandRequest('sheet', { + country: country, + content: content, + param: 'add/price' + }); +} diff --git a/src/lib/core/stores/adbWriter.ts b/src/lib/core/stores/adbWriter.ts index ec3f0a7..d36d54f 100644 --- a/src/lib/core/stores/adbWriter.ts +++ b/src/lib/core/stores/adbWriter.ts @@ -7,16 +7,27 @@ async function sendToAndroid(message: any) { let writer: any = get(adbWriter); console.log('writer', writer); if (!writer) { - // addNotification('ERR:No active connection'); - return; + addNotification('ERR:No active Android connection'); + return false; } try { const encoder = new TextEncoder(); - // console.log(writer); - await writer.write(encoder.encode(JSON.stringify(message) + '\n')); - console.log('sent! ', JSON.stringify(message).length); + const serializedMessage = JSON.stringify(message); + await writer.write(encoder.encode(serializedMessage + '\n')); + console.log('[ADB] sent', { + type: message?.type, + bytes: serializedMessage.length, + productCode: message?.payload?.data?.productCode, + batchCount: Array.isArray(message?.payload?.data) ? message.payload.data.length : undefined, + batchProductCodes: Array.isArray(message?.payload?.data) + ? message.payload.data.map((menu: any) => menu?.productCode) + : undefined + }); + return true; } catch (error) { console.error('write failed', error); + addNotification('ERR:Failed to send message to Android'); + return false; } } diff --git a/src/lib/core/stores/genLayoutStore.ts b/src/lib/core/stores/genLayoutStore.ts new file mode 100644 index 0000000..6053573 --- /dev/null +++ b/src/lib/core/stores/genLayoutStore.ts @@ -0,0 +1,249 @@ +import { writable, get } from 'svelte/store'; + +export interface GenLayoutFile { + file: string; + content: string; + file_index: number; +} + +export interface GenLayoutBatch { + batch_id: string; + total_files: number; + total_size_bytes: number; + status: 'idle' | 'generating' | 'receiving' | 'complete' | 'error'; + files: GenLayoutFile[]; + error?: string; +} + +// Track chunked file parts: Map> +interface ChunkedFileTracker { + file: string; + file_index: number; + total_parts: number; + parts: Map; +} + +const initialState: GenLayoutBatch = { + batch_id: '', + total_files: 0, + total_size_bytes: 0, + status: 'idle', + files: [] +}; + +export const genLayoutBatch = writable(initialState); + +// Track chunked files being assembled +const chunkedFiles = new Map(); + +// Callbacks for when batch completes +let onBatchCompleteCallback: ((files: GenLayoutFile[]) => void) | null = null; + +export function setOnBatchCompleteCallback(cb: (files: GenLayoutFile[]) => void) { + onBatchCompleteCallback = cb; +} + +export function clearOnBatchCompleteCallback() { + onBatchCompleteCallback = null; +} + +export function handleGenLayoutBatchStart(payload: { + batch_id: string; + total_files: number; + total_size_bytes: number; +}) { + genLayoutBatch.set({ + batch_id: payload.batch_id, + total_files: payload.total_files, + total_size_bytes: payload.total_size_bytes, + status: 'receiving', + files: [] + }); + console.log('[GenLayout] Batch started:', payload.batch_id, 'total files:', payload.total_files); +} + +export function handleGenLayoutFile(payload: { + batch_id: string; + file_index: number; + total_files: number; + file: string; + content: string; + is_chunked?: boolean; + part_index?: number; + total_parts?: number; + is_last_part?: boolean; +}) { + const batch = get(genLayoutBatch); + if (batch.batch_id !== payload.batch_id) return; + + if (payload.is_chunked) { + const fileIndex = payload.file_index; + const partIndex = payload.part_index ?? 0; + const totalParts = payload.total_parts ?? 1; + + // Get or create tracker for this file + let tracker = chunkedFiles.get(fileIndex); + if (!tracker) { + tracker = { + file: payload.file, + file_index: fileIndex, + total_parts: totalParts, + parts: new Map() + }; + chunkedFiles.set(fileIndex, tracker); + } + + // Store this chunk + tracker.parts.set(partIndex, payload.content); + + console.log( + '[GenLayout] Received chunk:', + partIndex + 1, + '/', + totalParts, + 'for file', + fileIndex + 1, + '/', + payload.total_files, + payload.file + ); + + // Check if all parts received + if (tracker.parts.size === totalParts) { + // Assemble the complete file content + const sortedParts: string[] = []; + for (let i = 0; i < totalParts; i++) { + sortedParts.push(tracker.parts.get(i) ?? ''); + } + const completeContent = sortedParts.join(''); + + // Add to files + addFileToStore(payload.batch_id, { + file: payload.file, + content: completeContent, + file_index: fileIndex + }, payload.total_files); + + // Clean up tracker + chunkedFiles.delete(fileIndex); + + console.log( + '[GenLayout] Assembled chunked file:', + fileIndex + 1, + '/', + payload.total_files, + payload.file + ); + } + } else { + // Handle non-chunked file + addFileToStore(payload.batch_id, { + file: payload.file, + content: payload.content, + file_index: payload.file_index + }, payload.total_files); + + console.log( + '[GenLayout] Received file:', + payload.file_index + 1, + '/', + payload.total_files, + payload.file + ); + } +} + +function addFileToStore(batchId: string, file: GenLayoutFile, totalFiles: number) { + genLayoutBatch.update((batch) => { + if (batch.batch_id !== batchId) return batch; + + const existingIndex = batch.files.findIndex((f) => f.file_index === file.file_index); + const newFiles = + existingIndex >= 0 + ? batch.files.map((f, index) => (index === existingIndex ? file : f)) + : [...batch.files, file]; + const sortedFiles = newFiles.sort((a, b) => a.file_index - b.file_index); + + return { + ...batch, + total_files: totalFiles || batch.total_files, + files: sortedFiles + }; + }); +} + +export function handleGenLayoutBatchEnd(payload: { batch_id: string; total_files: number }) { + const batch = get(genLayoutBatch); + + if (batch.batch_id !== payload.batch_id) return; + + const expectedTotal = payload.total_files || batch.total_files; + const sortedFiles = [...batch.files].sort((a, b) => a.file_index - b.file_index); + const missingIndexes = getMissingFileIndexes(sortedFiles, expectedTotal); + + if (missingIndexes.length > 0) { + // const error = `Gen Layout incomplete: received ${sortedFiles.length}/${expectedTotal} files. Missing file index: ${missingIndexes.join(', ')}`; + const error = `ไฟล์ไม่ครับ ทั้งหมด: ${sortedFiles.length}/${expectedTotal}` + genLayoutBatch.update((b) => ({ + ...b, + total_files: expectedTotal, + status: 'error', + files: sortedFiles, + error + })); + console.warn('[GenLayout] Batch incomplete:', error, sortedFiles); + return; + } + + genLayoutBatch.update((b) => ({ + ...b, + total_files: expectedTotal, + status: 'complete', + files: sortedFiles + })); + + console.log('[GenLayout] Batch complete, received', sortedFiles.length, 'files'); + + if (onBatchCompleteCallback) { + onBatchCompleteCallback(sortedFiles); + } +} + +function getMissingFileIndexes(files: GenLayoutFile[], totalFiles: number) { + if (totalFiles <= 0) return []; + + const receivedIndexes = new Set(files.map((file) => file.file_index)); + const indexes = [...receivedIndexes]; + const startsAtOne = indexes.length > 0 && Math.min(...indexes) >= 1; + const firstIndex = startsAtOne ? 1 : 0; + const lastIndex = startsAtOne ? totalFiles : totalFiles - 1; + const missingIndexes: number[] = []; + + for (let index = firstIndex; index <= lastIndex; index += 1) { + if (!receivedIndexes.has(index)) { + missingIndexes.push(index); + } + } + + return missingIndexes; +} + +export function handleGenLayoutError(error: string) { + genLayoutBatch.update((batch) => ({ + ...batch, + status: 'error', + error + })); +} + +export function resetGenLayoutBatch() { + genLayoutBatch.set(initialState); + chunkedFiles.clear(); +} + +export function setGenLayoutGenerating() { + genLayoutBatch.update((batch) => ({ + ...batch, + status: 'generating' + })); +} diff --git a/src/lib/core/stores/menuSaveStore.ts b/src/lib/core/stores/menuSaveStore.ts new file mode 100644 index 0000000..f6e465e --- /dev/null +++ b/src/lib/core/stores/menuSaveStore.ts @@ -0,0 +1,85 @@ +import { writable, get } from 'svelte/store'; + +export type MenuSaveStatus = 'idle' | 'saving' | 'saved' | 'error'; + +export interface MenuSaveState { + productCode: string; + status: MenuSaveStatus; + error?: string; + savedAt?: number; +} + +// Store for tracking menu save status +export const menuSaveStates = writable>(new Map()); + +// Callback to be called when a menu is saved successfully +let onMenuSavedCallback: ((productCode: string) => void) | null = null; + +export function setOnMenuSavedCallback(callback: (productCode: string) => void) { + onMenuSavedCallback = callback; +} + +export function clearOnMenuSavedCallback() { + onMenuSavedCallback = null; +} + +export function setMenuSaving(productCode: string) { + menuSaveStates.update((states) => { + const newStates = new Map(states); + newStates.set(productCode, { + productCode, + status: 'saving' + }); + return newStates; + }); +} + +export function setMenuSaved(productCode: string) { + menuSaveStates.update((states) => { + const newStates = new Map(states); + newStates.set(productCode, { + productCode, + status: 'saved', + savedAt: Date.now() + }); + return newStates; + }); + + // Call the callback if registered + if (onMenuSavedCallback) { + onMenuSavedCallback(productCode); + } +} + +export function setMenuSaveError(productCode: string, error: string) { + menuSaveStates.update((states) => { + const newStates = new Map(states); + newStates.set(productCode, { + productCode, + status: 'error', + error + }); + return newStates; + }); +} + +export function clearMenuSaveState(productCode: string) { + menuSaveStates.update((states) => { + const newStates = new Map(states); + newStates.delete(productCode); + return newStates; + }); +} + +export function getMenuSaveStatus(productCode: string): MenuSaveStatus { + const states = get(menuSaveStates); + return states.get(productCode)?.status ?? 'idle'; +} + +export function isMenuSaving(productCode: string): boolean { + return getMenuSaveStatus(productCode) === 'saving'; +} + +export function isMenuSaved(productCode: string): boolean { + return getMenuSaveStatus(productCode) === 'saved'; +} diff --git a/src/lib/core/stores/sheetStore.ts b/src/lib/core/stores/sheetStore.ts new file mode 100644 index 0000000..5a2e0dc --- /dev/null +++ b/src/lib/core/stores/sheetStore.ts @@ -0,0 +1,805 @@ +import { writable, get } from 'svelte/store'; + +// Catalog types +export interface Catalog { + catalog: string; + row_index: number; + status: 'free' | 'locked'; + locked_by: string | null; +} + +export interface CatalogsResponse { + status: string; + country: string; + catalogs: Catalog[]; +} + +export const sheetCatalogs = writable([]); +export const sheetCatalogsLoading = writable(false); + +export const countryPrimaryLanguageMap: Record = { + THAI: 'Thai', + tha: 'Thai', + cocktail_tha: 'Thai', + counter01: 'Thai', + MYS: 'Malaysia', + mys: 'Malaysia', + IDR: 'Indonesian', + idr: 'Indonesian', + AUS: 'English', + aus: 'English', + USA_PEPSI: 'English', + usa_pepsi: 'English', + SG: 'English', + SGP: 'English', + sgp: 'English', + UAE_DUBAI: 'Arabic', + uae_dubai: 'Arabic', + dubai: 'Arabic', + HKG: 'Cantonese', + hkg: 'Cantonese', + GBR: 'English', + gbr: 'English', + LTU: 'Lithuanian', + ltu: 'Lithuanian', + ROU: 'Romanian', + rou: 'Romanian', + LVA: 'Latvian', + lva: 'Latvian', + EST: 'Estonian', + est: 'Estonian' +}; + +export function getCountryPrimaryLanguage(countryCode: string): string { + return ( + countryPrimaryLanguageMap[countryCode] ?? + countryPrimaryLanguageMap[countryCode.toUpperCase()] ?? + 'Unknown' + ); +} + +// Sheet column configuration by country for new_layout_v2 +// Maps language keys to column indices and product code columns +export const SHEET_COLUMN_CONFIG_BY_COUNTRY: Record; + productCode: { hot: number; cold: number; blend: number }; + primaryLanguage: string; +}> = { + tha: { + language: { en: 3, th: 4, zh: 5, my: 8 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'th' + }, + aus: { + language: { en: 3, th: 4 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'en' + }, + gbr: { + language: { en: 3 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'en' + }, + hkg: { + language: { en: 3, zh_hans: 4, zh_hant: 5, th: 6 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'zh_hant' + }, + ltu: { + language: { en: 3, th: 4, lt: 5, ro: 6 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'lt' + }, + rou: { + language: { en: 3, th: 4, lt: 5, ro: 6 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'ro' + }, + lva: { + language: { en: 3, th: 4, lt: 5, ro: 6 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'en' + }, + est: { + language: { en: 3, th: 4, lt: 5, ro: 6 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'en' + }, + mys: { + language: { en: 3, th: 4, ms: 7 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'ms' + }, + sgp: { + language: { en: 3, th: 4 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'en' + }, + uae_dubai: { + language: { en: 3, ar: 4 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'ar' + }, + dubai: { + language: { en: 3, ar: 4 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'ar' + }, + default: { + language: { en: 3, th: 4 }, + productCode: { hot: 9, cold: 10, blend: 11 }, + primaryLanguage: 'en' + } +}; + +export function getSheetColumnConfig(countryCode: string) { + return SHEET_COLUMN_CONFIG_BY_COUNTRY[countryCode.toLowerCase()] + || SHEET_COLUMN_CONFIG_BY_COUNTRY.default; +} + +export function handleCatalogsResponse(content: CatalogsResponse) { + if (content && content.catalogs) { + sheetCatalogs.set(content.catalogs); + } + sheetCatalogsLoading.set(false); +} + +export interface SheetStreamMeta { + batch_id: string; + total_chunks: number; + total_items: number; + current_chunk: number; + status: 'idle' | 'streaming' | 'complete' | 'error'; + error?: string; +} + +export interface SheetMenuItem { + new_layout_v2: { + row_index: number; + cells: { value: string; coord: { row: number; col: number } }[]; + }[]; + name_desc_v2: { + key: string; + row_index: number; + cells: { value: string; coord: { row: number; col: number } }[]; + }[]; +} + +// Store for streaming metadata +export const sheetStreamMeta = writable(null); + +// Store for accumulated sheet data +export const sheetData = writable([]); + +// Store for loading state +export const sheetLoading = writable(false); + +// Store for error state +export const sheetError = writable(null); + +// Handler functions for sheet-service streaming +export function handleSheetStreamStart(payload: { + batch_id: string; + total_chunks: number; + total_items: number; + content: { message: string }; +}) { + sheetStreamMeta.set({ + batch_id: payload.batch_id, + total_chunks: payload.total_chunks, + total_items: payload.total_items, + current_chunk: 0, + status: 'streaming' + }); + sheetData.set([]); + sheetLoading.set(true); + sheetError.set(null); +} + +export function handleSheetStreamChunk(payload: { + batch_id: string; + current_chunk: number; + total_chunks: number; + total_items: number; + content: SheetMenuItem[]; +}) { + const meta = get(sheetStreamMeta); + + // Verify batch_id matches + if (meta && meta.batch_id === payload.batch_id) { + // Append new data + sheetData.update((current) => [...current, ...payload.content]); + + // Update progress + sheetStreamMeta.set({ + ...meta, + current_chunk: payload.current_chunk, + total_chunks: payload.total_chunks, + total_items: payload.total_items + }); + } +} + +export function handleSheetStreamEnd(payload: { + batch_id: string; + total_chunks: number; + total_items: number; + content: { message: string }; +}) { + const meta = get(sheetStreamMeta); + + if (meta && meta.batch_id === payload.batch_id) { + sheetStreamMeta.set({ + ...meta, + status: 'complete', + current_chunk: payload.total_chunks + }); + sheetLoading.set(false); + } +} + +export function handleSheetStreamError(payload: { + batch_id: string; + content: { error_detail: string }; +}) { + const meta = get(sheetStreamMeta); + + if (meta && meta.batch_id === payload.batch_id) { + sheetStreamMeta.set({ + ...meta, + status: 'error', + error: payload.content.error_detail + }); + sheetError.set(payload.content.error_detail); + sheetLoading.set(false); + } +} + +// Reset all sheet stores +export function resetSheetStore() { + sheetStreamMeta.set(null); + sheetData.set([]); + sheetLoading.set(false); + sheetError.set(null); +} + +// Store for existing product codes (for duplicate checking) +export const existingProductCodes = writable>(new Set()); +export const productCodesLoading = writable(false); + +// ───────────────────────────────────────── +// Sheet Price Streaming +// ───────────────────────────────────────── + +export interface GristCell { + coord: { + col: number; + row: number; + }; + value: string; +} + +export interface SheetPriceItem { + product_code: string; + cells: GristCell[]; +} + +// Price sheet header name mappings by country +// Maps our field names to the actual header names in the sheet +export const PRICE_HEADER_NAMES_BY_COUNTRY: Record = { + tha: { + cash_price: ['Price'], + non_cash_price: ['MainPrice'] + }, + mys: { + cash_price: ['F', 'Price'], + non_cash_price: ['MainPrice'] + }, + aus: { + cash_price: ['AUD', 'Price'], + non_cash_price: ['MainPrice'] + }, + sgp: { + cash_price: ['SGD', 'Price'], + non_cash_price: ['MainPrice'] + }, + hkg: { + cash_price: ['HK Final Px', 'Price'], + non_cash_price: ['MainPrice'] + }, + gbr: { + cash_price: ['GBR', 'Price'], + non_cash_price: ['MainPrice'] + }, + uae_dubai: { + cash_price: ['AED', 'Price'], + non_cash_price: ['MainPrice'] + }, + dubai: { + cash_price: ['AED', 'Price'], + non_cash_price: ['MainPrice'] + }, + ltu: { + cash_price: ['Price in Euro', 'Price'], + non_cash_price: ['MainPrice'] + }, + rou: { + cash_price: ['Price From LTU (EUR)', 'Price in RON'], + non_cash_price: ['MainPrice'] + }, + lva: { + cash_price: ['Price in Euro', 'Price'], + non_cash_price: ['MainPrice'] + }, + est: { + cash_price: ['Price in Euro', 'Price'], + non_cash_price: ['MainPrice'] + }, + // Default fallback for other countries + default: { + cash_price: ['Price', 'Price in Euro', 'CashPrice', 'AUD', 'SGD', 'GBR', 'AED', 'F'], + non_cash_price: ['MainPrice', 'NonCashPrice'] + } +}; + +// Find column index from header array by matching header names +export function findHeaderIndex(headerArray: string[], possibleNames: string[]): number { + for (const name of possibleNames) { + const idx = headerArray.findIndex(h => h.toLowerCase() === name.toLowerCase()); + if (idx !== -1) { + // Return col index (header index + 1 because cells start from col 1) + return idx + 1; + } + } + return -1; +} + +// Store: lastRequestSheetPrice[country][product_code] = cells (first row only, for display) +export const lastRequestSheetPrice = writable>>({}); + +// Store: sheetPriceHeader[country] = header array +export const sheetPriceHeader = writable>({}); + +// Store: sheetPriceAllRows[country][product_code] = array of {row, cells} (ALL rows for duplicates) +export const sheetPriceAllRows = writable>>({}); + +// Helper function to get price value from cells using dynamic header lookup +export function getPriceFromCells( + country: string, + cells: GristCell[], + priceType: 'cash_price' | 'non_cash_price' = 'cash_price' +): string | null { + const headers = get(sheetPriceHeader)[country]; + if (!headers || headers.length === 0) { + console.warn(`[getPriceFromCells] No header found for country: ${country}`); + return null; + } + + // Get possible header names for this country + const headerNames = PRICE_HEADER_NAMES_BY_COUNTRY[country] || PRICE_HEADER_NAMES_BY_COUNTRY.default; + const possibleNames = priceType === 'cash_price' ? headerNames.cash_price : headerNames.non_cash_price; + + // Find the column index for this price type + const colIdx = findHeaderIndex(headers, possibleNames); + //console.log(`[getPriceFromCells] ${country} ${priceType}: colIdx=${colIdx}, headers=`, headers, 'possibleNames=', possibleNames); + + if (colIdx < 0) { + console.warn(`[getPriceFromCells] No ${priceType} column found for ${country}, tried:`, possibleNames); + return null; + } + + // Find the cell with matching column index + const priceCell = cells.find((c) => c.coord?.col === colIdx); + //console.log(`[getPriceFromCells] Found cell for col ${colIdx}:`, priceCell); + return priceCell?.value ?? null; +} + +// Store for tracking streaming state +export const sheetPriceStreamMeta = writable<{ + request_id: string; + country: string; + status: 'idle' | 'streaming' | 'complete' | 'error'; + error?: string; +} | null>(null); + +export const sheetPriceLoading = writable(false); + +// Track sent request types (can only send once per type) +export const sheetPriceSentTypes = writable>(new Set()); + +// Raw streaming data accumulator +export const streamingRawData = writable< + Record< + string, + { + request_id: string; + header?: string[]; + country?: string; + chunks: any[]; + rawParts: string[]; // For accumulating raw JSON string parts + } + > +>({}); + +// Handler: raw_stream header (e.g., raw_stream_price) +export function handleRawStreamHeader(subtype: string, payload: any) { + console.log(`[RawStream] Header for ${subtype}:`, payload); + + // Get existing stream data to preserve country from request + const currentData = get(streamingRawData); + const existingData = currentData[subtype]; + + streamingRawData.update((data) => ({ + ...data, + [subtype]: { + request_id: payload.request_id, + header: payload.header || payload.headers, + country: payload.country || existingData?.country || '', + chunks: [], + rawParts: [] // Initialize for accumulating raw JSON string parts + } + })); + + if (subtype === 'price') { + sheetPriceStreamMeta.set({ + request_id: payload.request_id, + country: payload.country || existingData?.country || '', + status: 'streaming' + }); + sheetPriceLoading.set(true); + } +} + +// Handler: raw_stream chunk (e.g., raw_stream_chunk_price) +export function handleRawStreamChunk(subtype: string, payload: any) { + console.log(`[RawStream] Chunk ${payload.idx} for ${subtype}, raw length:`, payload.raw?.length); + + const currentData = get(streamingRawData); + const streamData = currentData[subtype]; + + if (!streamData || streamData.request_id !== payload.request_id) { + console.warn(`[RawStream] Chunk received for unknown stream: ${subtype}`); + return; + } + + // Check if data is in 'raw' field as JSON string (chunked) + if (payload.raw && typeof payload.raw === 'string') { + // Accumulate raw parts - will be joined and parsed in handleRawStreamEnd + streamingRawData.update((data) => ({ + ...data, + [subtype]: { + ...streamData, + country: payload.country || streamData.country, + rawParts: [...(streamData.rawParts || []), payload.raw] + } + })); + console.log(`[RawStream] Accumulated chunk ${payload.idx} for ${subtype}`); + return; + } + + // Handle non-chunked payload (already parsed content) + const content = payload.content || payload.data || payload.rows || []; + const contentArray = Array.isArray(content) ? content : [content]; + + streamingRawData.update((data) => ({ + ...data, + [subtype]: { + ...streamData, + country: payload.country || streamData.country, + chunks: [...streamData.chunks, ...contentArray] + } + })); + + console.log(`[RawStream] Chunk for ${subtype}: +${contentArray.length} items`); +} + +// Handler: raw_stream end (e.g., raw_stream_end_price) +export function handleRawStreamEnd(subtype: string, payload: any) { + console.log(`[RawStream] End payload for ${subtype}:`, payload); + + const currentData = get(streamingRawData); + const streamData = currentData[subtype]; + + if (!streamData || streamData.request_id !== payload.request_id) { + console.warn(`[RawStream] End received for unknown stream: ${subtype}`); + return; + } + + // Get country from stored stream data or payload + const country = streamData.country || payload.country || ''; + + // If we have accumulated raw parts, join and parse them now + let chunks = streamData.chunks || []; + if (streamData.rawParts && streamData.rawParts.length > 0) { + const fullRawJson = streamData.rawParts.join(''); + console.log( + `[RawStream] Joining ${streamData.rawParts.length} raw parts, total length: ${fullRawJson.length}` + ); + + try { + const parsed = JSON.parse(fullRawJson); + console.log(`[RawStream] Parsed combined raw data, keys:`, Object.keys(parsed)); + + // Extract content from nested structure: { payload: { content: [...] } } + const content = parsed?.payload?.content || parsed?.content || parsed || []; + chunks = Array.isArray(content) ? content : [content]; + + // Log first item to see actual structure + if (chunks.length > 0) { + console.log(`[RawStream] First content item:`, JSON.stringify(chunks[0]).substring(0, 200)); + } + } catch (e) { + console.error(`[RawStream] Failed to parse combined raw JSON:`, e); + } + } + + console.log(`[RawStream] End for ${subtype}: total ${chunks.length} items, country: ${country}`); + + if (subtype === 'price') { + processSheetPriceData(country, streamData.header || [], chunks); + sheetPriceStreamMeta.update((meta) => (meta ? { ...meta, status: 'complete' } : null)); + sheetPriceLoading.set(false); + } + + // Clear the streaming data + streamingRawData.update((data) => { + const newData = { ...data }; + delete newData[subtype]; + return newData; + }); +} + +// Process and store sheet price data +function processSheetPriceData(country: string, header: string[], chunks: any[]) { + console.log(`[SheetPrice] Processing data for ${country}:`, { + header, + chunksCount: chunks.length + }); + console.log(`[SheetPrice] Sample chunk:`, chunks[0]); + + // Try to extract header from first chunk item if not provided separately + // Backend sends header embedded in each item: { header: [...], key: "...", payload: [...] } + let effectiveHeader = header; + if ((!effectiveHeader || effectiveHeader.length === 0) && chunks.length > 0) { + const firstChunk = chunks[0]; + if (firstChunk?.header && Array.isArray(firstChunk.header)) { + effectiveHeader = firstChunk.header; + console.log(`[SheetPrice] Extracted header from first chunk:`, effectiveHeader); + } + } + + // Save header for dynamic column lookup later + if (effectiveHeader && effectiveHeader.length > 0) { + sheetPriceHeader.update((data) => ({ + ...data, + [country]: effectiveHeader + })); + console.log(`[SheetPrice] Saved header for ${country}:`, effectiveHeader); + } + + // Find column indices dynamically from header + // product_code header is typically "ProductCode" or similar + const productCodeIdx = findHeaderIndex(effectiveHeader, ['ProductCode', 'Product_Code', 'product_code', 'Code']); + console.log(`[SheetPrice] productCodeIdx from header:`, productCodeIdx, 'header:', effectiveHeader); + + const priceByProductCode: Record = {}; + // Track ALL rows per product code (for duplicates) + const allRowsByProductCode: Record = {}; + + for (const row of chunks) { + if (!row) { + console.log(`[SheetPrice] Row is null/undefined`); + continue; + } + + // Support new structure: {key, payload} where key=product_code + // payload can be: [{cells: [...], row_index: ...}, ...] - multiple entries for duplicates + if (row.key !== undefined) { + const productCode = row.key; + + // Handle payload structure - iterate ALL entries in payload for duplicates + if (Array.isArray(row.payload) && row.payload.length > 0) { + // Check if payload[0] has cells property (nested structure with row_index) + if (row.payload[0]?.cells) { + // payload: [{cells: [...], row_index: ...}, {cells: [...], row_index: ...}, ...] + // Store first one for backward compatibility + priceByProductCode[productCode] = row.payload[0].cells; + + // Store ALL rows for duplicate handling + if (!allRowsByProductCode[productCode]) { + allRowsByProductCode[productCode] = []; + } + for (const entry of row.payload) { + if (entry.cells && entry.row_index !== undefined) { + allRowsByProductCode[productCode].push({ + row: entry.row_index, + cells: entry.cells + }); + } + } + } else if (row.payload[0]?.coord) { + // payload: [{coord: {...}, value: ...}, ...] - flat cells array + priceByProductCode[productCode] = row.payload; + // Extract row from first cell's coord + const rowNum = row.payload[0]?.coord?.row; + if (rowNum !== undefined) { + allRowsByProductCode[productCode] = [{ row: rowNum, cells: row.payload }]; + } + } + } + continue; + } + + // Fallback: Check if row has cells or if row itself is the cells array + let cells: GristCell[] = row.cells || row; + + if (!Array.isArray(cells)) { + console.log(`[SheetPrice] Unknown row structure:`, row); + continue; + } + + // Find product_code from cells by column index + if (productCodeIdx >= 0) { + const productCodeCell = cells.find((c: GristCell) => c.coord?.col === productCodeIdx); + const productCode = productCodeCell?.value; + + if (productCode) { + priceByProductCode[productCode] = cells; + // Extract row from first cell's coord + const rowNum = cells[0]?.coord?.row; + if (rowNum !== undefined) { + if (!allRowsByProductCode[productCode]) { + allRowsByProductCode[productCode] = []; + } + allRowsByProductCode[productCode].push({ row: rowNum, cells }); + } + } + } + } + + lastRequestSheetPrice.update((data) => ({ + ...data, + [country]: { + ...(data[country] || {}), + ...priceByProductCode + } + })); + + // Update sheetPriceAllRows for duplicate handling + sheetPriceAllRows.update((data) => ({ + ...data, + [country]: { + ...(data[country] || {}), + ...allRowsByProductCode + } + })); + + console.log( + `[SheetPrice] Processed ${Object.keys(priceByProductCode).length} prices for ${country}` + ); + console.log(`[SheetPrice] Sample product codes:`, Object.keys(priceByProductCode).slice(0, 5)); + // Log duplicates info + const duplicates = Object.entries(allRowsByProductCode).filter(([_, rows]) => rows.length > 1); + if (duplicates.length > 0) { + console.log(`[SheetPrice] Found ${duplicates.length} product codes with duplicate rows:`, duplicates.slice(0, 3)); + } + if (chunks.length > 0 && Object.keys(priceByProductCode).length > 0) { + const sampleKey = Object.keys(priceByProductCode)[0]; + console.log(`[SheetPrice] Sample cells for ${sampleKey}:`, priceByProductCode[sampleKey]); + } +} + +// Reset sheet price stores +export function resetSheetPriceStore() { + sheetPriceStreamMeta.set(null); + sheetPriceLoading.set(false); + streamingRawData.set({}); +} + +// Check if a request type has already been sent +export function hasSheetPriceBeenSent(type: string): boolean { + return get(sheetPriceSentTypes).has(type); +} + +// Mark a request type as sent +export function markSheetPriceAsSent(type: string) { + sheetPriceSentTypes.update((types) => { + const newTypes = new Set(types); + newTypes.add(type); + return newTypes; + }); +} + +// Clear sent types (for reset) +export function clearSheetPriceSentTypes() { + sheetPriceSentTypes.set(new Set()); +} + +/** + * Add a newly generated product code to the store (local tracking before server sync) + */ +export function addGeneratedProductCode(code: string) { + existingProductCodes.update((codes) => { + const newSet = new Set(codes); + newSet.add(code); + return newSet; + }); + console.log('[sheetStore] Added generated code:', code); +} + +const PRODUCT_CODES_STORAGE_KEY = 'supra_product_codes'; + +// Track current/pending country for product codes +let currentProductCodesCountry = ''; +let pendingProductCodesCountry = ''; + +// Set pending country when making a request +export function setPendingProductCodesCountry(country: string) { + pendingProductCodesCountry = country; + console.log('[sheetStore] Pending product codes country:', country); +} + +// Load product codes from localStorage for specific country +export function loadProductCodesFromCache(country?: string): boolean { + try { + const cached = localStorage.getItem(PRODUCT_CODES_STORAGE_KEY); + if (cached) { + const data = JSON.parse(cached); + // Only load if country matches (or no country filter specified) + if (data.codes && Array.isArray(data.codes)) { + if (country && data.country && data.country !== country) { + console.log('[sheetStore] Cache is for different country:', data.country, '!= requested:', country); + // Clear the store for different country + existingProductCodes.set(new Set()); + return false; + } + existingProductCodes.set(new Set(data.codes)); + currentProductCodesCountry = data.country || ''; + console.log('[sheetStore] Loaded', data.codes.length, 'product codes from cache for', data.country || 'unknown'); + return true; + } + } + } catch (e) { + console.warn('[sheetStore] Failed to load from cache:', e); + } + // Clear store if no valid cache + existingProductCodes.set(new Set()); + return false; +} + +// Clear product codes (call when switching countries) +export function clearProductCodes() { + existingProductCodes.set(new Set()); + currentProductCodesCountry = ''; + console.log('[sheetStore] Cleared product codes'); +} + +export function handleListMenuResponse(payload: { codes: string[]; country?: string }) { + // Use pending country if not in payload + const country = payload.country || pendingProductCodesCountry; + console.log('[sheetStore] Received list_menu_response for', country, ':', payload.codes?.length, 'codes'); + + if (payload && payload.codes) { + existingProductCodes.set(new Set(payload.codes)); + currentProductCodesCountry = country; + + // Save to localStorage with country + try { + localStorage.setItem( + PRODUCT_CODES_STORAGE_KEY, + JSON.stringify({ + codes: payload.codes, + country: country, + timestamp: Date.now() + }) + ); + console.log('[sheetStore] Saved', payload.codes.length, 'product codes to cache for', country); + } catch (e) { + console.warn('[sheetStore] Failed to save to cache:', e); + } + } + productCodesLoading.set(false); +} diff --git a/src/lib/core/stores/websocketStore.ts b/src/lib/core/stores/websocketStore.ts index 6d9271d..d43e064 100644 --- a/src/lib/core/stores/websocketStore.ts +++ b/src/lib/core/stores/websocketStore.ts @@ -16,6 +16,38 @@ export const socketConnectionOfflineCount = writable(0); export const socketAlreadySendHeartbeat = writable(0); export const socketStore = writable(null); +export function waitForOpenSocket(timeoutMs = 8000): Promise { + const currentSocket = get(socketStore); + if (currentSocket?.readyState === WebSocket.OPEN) { + return Promise.resolve(currentSocket); + } + + return new Promise((resolve) => { + let settled = false; + let unsubscribe = () => {}; + const timeout = setTimeout(() => { + if (settled) return; + settled = true; + unsubscribe(); + resolve(null); + }, timeoutMs); + + function settle(nextSocket: WebSocket | null) { + if (settled) return; + settled = true; + clearTimeout(timeout); + unsubscribe(); + resolve(nextSocket); + } + + unsubscribe = socketStore.subscribe((nextSocket) => { + if (nextSocket?.readyState === WebSocket.OPEN) { + settle(nextSocket); + } + }); + }); +} + export function connectToWebsocket(id_token?: string) { if (browser) { // console.log('connecting to ', env.PUBLIC_WSS); @@ -41,6 +73,13 @@ export function connectToWebsocket(id_token?: string) { let auth_data = get(authStore); let perms = get(permission); + // Debug: check if auth_data has uid + console.log('[WS Auth] Sending auth with:', { + uid: auth_data?.uid, + name: auth_data?.displayName, + email: auth_data?.email + }); + sendMessage({ type: 'auth', payload: { @@ -53,6 +92,7 @@ export function connectToWebsocket(id_token?: string) { } }); } + console.log(socket); // heartbeat 10s setInterval(() => { diff --git a/src/lib/core/types/catalogData.ts b/src/lib/core/types/catalogData.ts new file mode 100644 index 0000000..94bd076 --- /dev/null +++ b/src/lib/core/types/catalogData.ts @@ -0,0 +1,12 @@ +export interface MenuItem { + row_index: number; + name_th: string; + name_en: string; + product_codes: string[]; +} + +export interface CatalogDetail { + country: string; + catalog: string; + items: MenuItem[]; +} diff --git a/src/lib/core/types/outMessage.ts b/src/lib/core/types/outMessage.ts index afe8c23..ff24f95 100644 --- a/src/lib/core/types/outMessage.ts +++ b/src/lib/core/types/outMessage.ts @@ -53,6 +53,15 @@ export type OutMessage = values: any; }; } + | { + type: 'list_menu'; + payload: { + user_info: any; + country: string; + boxid?: string; + }; + } + | { type: 'price'; payload: { diff --git a/src/lib/core/utils/productCode.ts b/src/lib/core/utils/productCode.ts new file mode 100644 index 0000000..0194d66 --- /dev/null +++ b/src/lib/core/utils/productCode.ts @@ -0,0 +1,162 @@ +import { get } from 'svelte/store'; +import { existingProductCodes } from '../stores/sheetStore'; + +// Country code mapping (country short from Android -> 2-digit product code prefix) +// Country short is read from /sdcard/coffeevending/country/short +export const countryCodeMap: Record = { + // Thailand + THAI: '12', + tha: '12', + // Malaysia + MYS: '12', + mys: '12', + // Indonesia + IDR: '13', + idr: '13', + // Australia + AUS: '51', + aus: '51', + // Singapore + SGP: '52', + sgp: '52', + SG: '52', // obsolete + // UAE Dubai + UAE_DUBAI: '53', + uae_dubai: '53', + dubai: '53', + // Hong Kong + HKG: '54', + hkg: '54', + // UK + GBR: '55', + gbr: '55', + // Romania + ROU: '56', + rou: '56', + // Latvia + LVA: '57', + lva: '57', + // Estonia + EST: '58', + est: '58', + // Lithuania + LTU: '59', + ltu: '59', + // USA Pepsi (uses Thai prefix) + USA_PEPSI: '12', + usa_pepsi: '12', + // Counter machines + counter01: '19' +}; + +// Temperature codes +export const tempCodes: Record = { + hot: '01', + cold: '02', + blend: '03' +}; + +// Category/Drink type codes +export const categoryOptions = [ + { value: '01', label: 'Coffee V1' }, + { value: '21', label: 'Coffee V2' }, + { value: '02', label: 'Tea' }, + { value: '03', label: 'Milk' }, + { value: '04', label: 'Whey' }, + { value: '05', label: 'Soda & Other' } +] as const; + +export type TempType = 'hot' | 'cold' | 'blend'; +export type CategoryCode = (typeof categoryOptions)[number]['value']; + +/** + * Get all existing product code suffixes (last 4 digits) + * @param category - Optional category code to filter by (e.g., '01' for Coffee V1) + */ +export function getExistingCodeSuffixes(category?: string): Set { + const codes = get(existingProductCodes); + + if (category) { + // Filter codes by category (2nd segment: XX-[category]-XX-XXXX) + return new Set( + [...codes] + .filter((code) => { + const parts = code.split('-'); + return parts[1] === category; + }) + .map((code) => code.split('-')[3]) + ); + } + + return new Set([...codes].map((code) => code.split('-')[3])); +} + +/** + * Generate next suffix by finding max existing + 1 for a specific category + * @param category - Optional category code to find max within (e.g., '01' for Coffee V1) + */ +export function generateNextSuffix(category?: string): string { + const suffixes = getExistingCodeSuffixes(category); + let maxSuffix = 0; + + for (const suffix of suffixes) { + const num = parseInt(suffix, 10); + if (!isNaN(num) && num > maxSuffix) { + maxSuffix = num; + } + } + + const nextSuffix = maxSuffix + 1; + + if (nextSuffix > 9999) { + throw new Error('Product code suffix exceeded 9999'); + } + + return String(nextSuffix).padStart(4, '0'); +} + +/** + * Generate a complete product code + * @param country - Country code (e.g., 'tha') + * @param category - Category code (e.g., '01' for Coffee V1) + * @param temp - Temperature type ('hot', 'cold', 'blend') + * @param suffix - Optional specific suffix, otherwise auto-generate + * @returns Product code like '12-01-01-0006' + */ +export function generateProductCode( + country: string, + category: string, + temp: TempType, + suffix?: string +): string { + const countryCode = countryCodeMap[country] || '99'; + const tempCode = tempCodes[temp]; + const codeSuffix = suffix ?? generateNextSuffix(); + + return `${countryCode}-${category}-${tempCode}-${codeSuffix}`; +} + +/** + * Check if a product code already exists + */ +export function isProductCodeExists(code: string): boolean { + const codes = get(existingProductCodes); + return codes.has(code); +} + +/** + * Generate a unique product code (auto-retry if exists) + */ +export function generateUniqueProductCode( + country: string, + category: string, + temp: TempType +): string { + const code = generateProductCode(country, category, temp); + + if (isProductCodeExists(code)) { + throw new Error(`Product code already exists: ${code}`); + } + + return code; +} diff --git a/src/lib/data/productcode.txt b/src/lib/data/productcode.txt new file mode 100644 index 0000000..fa6c13c --- /dev/null +++ b/src/lib/data/productcode.txt @@ -0,0 +1 @@ +["12-01-01-0003", "12-01-01-0001", "12-11-01-0001", "12-01-01-0004", "12-01-01-0005", "12-03-01-0005", "12-01-01-0006", "12-03-01-0002", "12-05-01-0002", "12-11-02-0001", "12-01-02-0001", "12-01-02-0002", "12-11-02-0002", "12-01-02-0003", "12-03-02-0002", "12-02-02-0002", "12-01-02-0004", "12-05-01-0001", "12-02-01-0007", "12-02-02-0003", "12-02-02-0007", "12-05-02-0010", "12-05-02-0007", "12-05-02-0021", "12-05-02-0020", "12-05-02-0036", "12-01-02-0018", "12-05-02-0035", "12-05-02-0037", "12-05-02-0034", "12-05-02-0001", "12-02-01-0003", "12-03-02-0006", "12-05-02-0002", "12-05-02-0003", "12-05-02-0009", "12-05-02-0008", "12-05-02-0015", "12-03-02-0001", "12-02-02-0010", "12-02-02-0009", "12-03-01-0004", "12-03-01-0001", "12-04-02-0002", "12-05-02-0017", "12-05-02-0014", "12-02-01-0001", "12-02-02-0001", "12-05-02-0018", "12-05-02-0016", "12-01-01-0007", "12-01-01-0009", "12-01-01-0010", "12-01-02-0006", "12-01-02-0007", "12-02-02-0004", "12-05-02-0019", "12-05-02-0012", "12-05-02-0011", "12-05-02-0013", "12-05-02-0004", "12-02-01-0004", "12-02-02-0008", "12-02-02-0006", "12-05-02-0006", "12-01-01-0008", "12-01-01-0011", "12-01-01-0012", "12-03-01-0003", "12-02-01-0002", "12-02-01-0006", "12-02-01-0008", "12-02-01-0009", "12-02-01-0010", "12-04-02-0001", "12-01-02-0005", "12-01-02-0008", "12-01-02-0009", "12-03-02-0003", "12-03-02-0004", "12-03-02-0005", "12-04-02-0003", "12-04-02-0008", "12-04-02-0004", "12-04-02-0005", "12-04-02-0006", "12-04-02-0007", "12-04-02-0009", "12-04-02-0010", "12-04-02-0011", "12-02-01-0011", "12-02-02-0012", "12-02-01-0012", "12-03-02-0007", "12-02-02-0011", "12-01-01-0014", "12-01-02-0011", "12-05-02-0022", "12-01-01-0013", "12-05-02-0023", "12-02-02-0013", "12-01-02-0012", "12-02-02-0015", "12-02-01-0016", "12-02-01-0017", "12-02-02-0017", "12-05-02-0024", "12-05-02-0025", "12-05-02-0026", "12-05-02-0027", "12-05-02-0028", "12-05-02-0029", "12-05-02-0030", "12-05-02-0031", "12-05-02-0032", "12-05-02-0033", "12-02-01-0019", "12-02-02-0019", "12-01-01-0017", "12-01-02-0017", "1", "12-04-02-0012", "12-02-01-0020", "12-02-01-0021", "12-02-02-0020", "12-05-02-0038", "12-05-02-0039", "12-05-02-0041", "12-04-02-0013", "12-02-02-0022", "12-02-01-0022", "12-01-02-0020", "12-01-01-0019", "12-01-02-0019", "12-01-01-0020", "12-03-03-0009", "12-01-02-0022", "12-01-01-0021", "12-01-02-0021", "12-01-01-0022", "12-03-02-0010", "12-02-02-0023", "12-03-02-0011", "12-04-02-0014", "12-05-01-0040", "12-06-01-0001", "12-06-01-0002", "12-01-02-0026", "12-01-01-0025", "12-01-02-0025", "12-01-01-0026", "12-01-02-0024", "12-01-01-0023", "12-01-02-0023", "12-01-01-0024", "12-01-02-0028", "12-01-01-0027", "12-01-02-0027", "12-01-01-0028", "12-01-01-0029", "12-01-01-0030", "12-01-01-0031", "12-01-02-0029", "12-01-02-0030", "12-01-02-0031", "12-02-01-0024", "12-02-01-0025", "12-02-01-0026", "12-02-01-0027", "12-02-01-0028", "12-02-02-0024", "12-02-02-0025", "12-02-02-0026", "12-02-02-0027", "12-02-02-0028", "12-02-02-0029", "12-02-02-0030", "12-02-02-0031", "12-02-02-0032", "12-04-02-0015", "12-04-02-0016", "12-04-02-0017", "12-04-02-1018", "12-04-02-1019", "12-04-02-1020", "12-04-02-1021", "12-04-02-1022", "12-01-02-0033", "12-01-01-0032", "12-01-02-0032", "12-01-01-0033", "12-01-01-0034", "12-01-02-0034", "12-02-01-0033", "12-02-01-0034", "12-02-02-0033", "12-02-02-0034", "12-03-01-0012", "12-03-02-0012", "12-05-01-0042", "12-05-02-0042", "12-05-02-0043", "12-02-01-0032", "12-02-01-0031", "12-03-02-0013", "12-02-01-0029", "12-02-01-0030", "12-01-02-0036", "12-01-01-0035", "12-01-02-0035", "12-01-01-0036", "12-03-03-0020", "12-03-01-0019", "12-03-02-0019", "12-01-02-0037", "12-01-01-0038", "12-01-02-0038", "12-01-01-0037", "12-03-03-0021", "12-03-01-0022", "12-03-02-0022", "12-05-02-0049", "12-05-02-0048", "12-05-02-0050", "12-05-02-0046", "12-05-02-0047", "12-01-05-0044", "12-05-02-0044", "12-01-01-0039", "12-01-02-0039", "12-02-01-0035", "12-04-02-0018", "12-01-02-0041", "12-05-02-0045", "12-02-01-0036", "12-02-02-0036", "12-01-01-1003", "12-01-01-1004", "12-01-02-1001", "12-01-02-1002", "12-01-02-1003", "12-02-02-1025", "12-02-02-1026", "12-02-02-1027", "12-02-02-1030", "12-02-02-1024", "12-03-01-1002", "12-03-02-1002", "12-05-02-1010", "12-01-01-1006", "12-02-01-1024", "12-02-01-1025", "12-02-01-1026", "12-02-01-1029", "12-02-01-1030", "12-02-01-1031", "12-02-01-1027", "12-02-02-1029", "12-02-02-1031", "12-03-01-1005", "12-03-02-1005", "12-05-01-1001", "12-05-02-1001", "12-01-02-1099", "12-01-01-1099", "12-21-01-0001", "12-21-01-0004", "12-21-01-0005", "12-51-01-0005", "12-07-01-0005", "12-21-01-0006", "12-21-01-0007", "12-21-01-0008", "12-21-01-0009", "12-21-01-0010", "12-21-01-0011", "12-21-01-0012", "12-21-01-0029", "12-21-01-0030", "12-21-01-0031", "12-21-01-0039", "12-21-02-0001", "12-21-02-0002", "12-21-02-0003", "12-21-02-0004", "12-21-02-0005", "12-21-02-0006", "12-21-02-0007", "12-21-02-0008", "12-21-02-0029", "12-21-02-0030", "12-21-02-0031", "12-21-02-0039", "12-21-02-0041", "12-31-01-0001", "12-31-02-0001", "12-31-02-0002", "12-21-01-0003", "12-21-01-0017", "12-21-02-0017", "12-02-01-0037", "12-02-02-0037", "12-02-01-1037", "12-02-02-1037", "12-21-01-1003", "12-21-01-1004", "12-21-01-1006", "12-21-01-1099", "12-21-02-1001", "12-21-02-1002", "12-21-02-1003", "12-21-02-1099", "12-01-01-0040", "12-01-02-0040", "12-02-01-0038", "12-02-02-0038", "12-03-02-0025", "12-03-01-0025", "12-01-01-0042", "12-01-02-0042", "12-21-01-0040", "12-21-02-0040", "12-05-03-0040", "12-01-02-0045", "12-01-01-0044", "12-01-02-0044", "12-01-01-0043", "12-03-01-0023", "12-03-01-0024", "12-03-02-0023", "12-03-02-0024", "12-21-01-0042", "12-21-02-0042", "12-03-03-0027", "12-03-03-0028", "12-03-03-0029", "12-03-03-0030", "12-03-03-0031", "12-01-03-0043", "12-02-03-0044", "12-99-03-0040", "12-05-02-0053", "12-05-02-0054", "12-01-02-0048", "12-01-01-0047", "12-01-02-0047", "12-01-01-0046", "12-01-02-0051", "12-01-01-0050", "12-01-02-0050", "12-01-01-0049", "12-01-02-0054", "12-01-02-0053", "12-02-02-0043", "12-02-03-0043", "12-02-02-0044", "12-03-02-0026", "12-05-02-0055", "12-05-02-0056", "12-02-01-0041", "12-02-01-0042", "12-02-02-0041", "12-02-02-0042", "12-05-02-0051", "12-05-02-0052", "12-03-03-0032", "12-02-01-0045", "12-99-01-0042", "12-02-01-0046", "12-99-01-0043", "12-02-02-0045", "12-99-02-0044", "12-02-02-0046", "12-99-02-0045", "12-02-01-0039", "12-02-02-0039", "12-02-02-0040", "12-99-03-0041", "12-02-01-0040", "12-02-01-0047", "12-02-01-1038", "12-02-01-1039", "12-02-01-1040", "12-02-02-1038", "12-02-02-1039", "12-02-02-1040", "12-02-02-0047", "12-01-01-0055", "12-01-02-0055", "12-04-02-0055", "12-21-01-0055", "12-21-02-0055", "12-99-02-0009", "12-99-02-0002", "12-99-02-0006", "12-99-02-0007", "12-99-02-0008", "12-99-02-0001", "12-99-02-0003", "12-99-02-0005", "12-99-01-0010", "12-99-02-0011", "12-99-02-0012", "12-99-02-0013", "12-99-02-0014", "12-99-02-0015", "12-99-02-0016", "12-99-03-0017", "12-99-02-0018", "12-99-02-0019", "12-99-03-0020", "12-99-03-0021", "12-99-03-0022", "12-99-03-0023", "12-99-03-0024", "12-99-03-0025", "12-99-03-0026", "12-99-03-0027", "12-99-03-0028", "12-99-03-0029", "12-99-03-0030", "12-99-03-0031", "12-99-03-0033", "12-99-03-0034", "12-99-03-0035", "12-99-03-0036", "12-99-03-0038", "12-99-03-0037", "12-99-03-0039", "12-99-03-0047", "12-02-03-0048", "12-99-03-0048", "12-02-01-0048", "12-99-01-0049", "12-02-02-0049", "12-99-02-0050", "12-05-02-0059", "12-99-05-0051", "12-05-02-0061", "12-99-05-0052", "12-05-02-0062", "12-01-02-0058", "12-05-02-0060", "12-05-02-0063", "12-21-01-0057", "12-21-02-0057", "12-01-02-0057", "12-02-02-0050", "12-05-02-0064", "12-05-02-0065", "12-21-02-0058", "12-99-05-0053", "12-99-05-0054", "12-99-05-0055", "12-99-05-0056", "12-99-05-0057", "12-99-05-0058", "12-99-05-0059", "12-99-05-0060", "12-99-05-0061", "12-99-05-0062", "12-99-05-0063", "12-99-05-0064", "12-99-05-0065", "12-99-03-0066", "12-01-01-0057", "12-01-01-0058", "12-21-01-0058", "12-02-01-0055", "12-02-01-0050", "12-03-01-0035", "12-03-01-0036", "12-05-02-0066", "12-05-02-0067", "12-03-02-0035", "12-03-02-0036", "12-03-02-0037", "12-03-82-0041", "12-03-82-0042", "12-03-82-0043", "12-03-83-0041", "12-03-83-0042", "12-03-83-0043", "12-01-01-0059", "12-21-01-0059", "12-01-02-0059", "12-21-02-0059", "12-01-01-0060", "12-21-01-0060", "12-02-01-0051", "12-02-02-0051", "12-03-01-0038", "12-03-02-0038", "12-05-02-0069", "12-03-02-0039", "12-05-02-0070", "12-05-02-0071", "12-05-02-0072", "12-02-02-0052", "12-03-03-0040", "12-05-02-0068", "12-01-02-0060", "12-21-02-0060", "12-05-02-0073", "12-03-03-0034", "12-41-03-0001", "12-41-03-0002", "12-41-03-0003", "12-41-03-0004", "12-41-03-0005", "12-41-03-0006", "12-41-03-0007", "12-41-03-0008", "12-41-03-0009", "12-41-03-0010", "12-41-03-0011", "12-41-03-0012", "12-41-03-0013", "12-41-03-0014", "12-41-03-0015", "12-41-03-0016", "12-41-03-0017", "12-41-03-0018", "12-41-03-0019", "12-41-03-0020", "12-41-03-0021", "12-41-03-0022", "12-41-03-0023", "12-41-03-0024", "12-41-03-0025", "12-03-03-0041", "12-03-03-0042", "12-03-03-0043", "12-05-02-0078", "12-02-02-0055", "12-01-02-0062", "12-02-02-0053", "12-02-02-0054", "12-03-03-0044", "12-03-03-0045", "12-01-02-0061", "12-21-02-0062", "12-21-02-0061", "12-41-03-0026", "12-21-62-0002", "12-21-62-0003", "12-21-62-0004", "12-03-62-0002", "12-03-62-0005", "12-03-62-0006", "12-05-62-0007", "12-05-62-0009", "12-05-62-0025", "12-05-62-0035", "12-05-62-0046", "12-02-62-0026", "12-02-62-0025", "12-02-62-0039", "12-02-62-0047", "12-41-03-0028", "12-02-62-0031", "12-41-03-0027", "12-01-02-0068", "12-21-02-0068", "12-03-01-0058", "12-03-02-0058", "12-03-03-0058", "12-03-01-0059", "12-03-02-0059", "12-03-03-0059", "12-02-02-0060", "12-02-02-0061", "12-02-02-0062", "12-02-02-0063", "12-02-02-0064", "12-02-02-0065", "12-05-02-0090", "12-05-02-0091", "12-02-03-0060", "12-02-03-0061", "12-02-03-0062", "12-02-03-0063", "12-02-03-0064", "12-02-03-0065", "12-02-01-0061", "12-02-01-0062", "12-02-01-0063", "12-02-01-0064", "12-02-01-0065", "12-03-01-0048", "12-03-02-0048", "12-03-03-0049", "12-05-02-0094", "12-05-02-0095", "12-05-02-0093", "12-05-03-0092", "12-05-02-0096", "12-05-02-0097", "12-01-02-0069", "12-01-02-0070", "12-21-02-0069", "12-21-02-0070", "12-05-02-0100", "12-05-02-0101", "12-05-02-0102", "12-05-02-0103", "12-05-02-0115", "12-05-02-0116", "12-05-02-0104", "12-05-02-0105", "12-05-02-0106", "12-05-02-0107", "12-05-02-0108", "12-05-02-0109", "12-05-02-0114", "12-03-03-0060", "12-05-02-0117", "12-01-02-0064", "12-21-02-0064", "12-21-02-0071", "12-03-03-0061", "12-04-02-0056", "12-04-02-0057", "12-04-02-0058", "12-99-03-0068", "12-05-02-0118", "12-05-02-0119", "12-05-02-0120", "12-03-02-0065", "12-03-02-0066", "12-03-02-0067", "12-03-02-0068", "12-03-02-0069", "12-03-01-0068", "12-05-02-0121", "12-03-02-0070", "12-03-02-0071", "12-03-02-0074", "12-21-02-0073", "12-03-02-0073", "12-02-02-0067", "12-21-02-0074", "12-01-02-0074", "12-01-02-0073", "12-05-03-0064", "12-02-02-0056", "12-03-03-0046", "12-02-03-0053", "12-05-03-0115", "12-21-02-0075", "12-01-02-0075", "12-03-02-0075", "12-05-02-0124", "12-02-02-0068", "12-05-02-0125", "12-03-02-0076", "12-02-02-0069", "12-05-02-0126", "12-05-02-0127", "12-02-02-0070", "12-99-05-0073", "12-03-03-0075", "12-02-03-0068", "12-05-03-0124", "12-03-03-0076", "12-02-03-0069", "12-05-03-0126", "12-05-03-0127", "12-02-03-0070", "12-01-02-0081", "12-21-02-0081", "12-01-01-0081", "12-21-01-0081", "12-02-02-0080", "12-05-03-0128", "12-03-03-0084", "12-03-03-0085", "12-02-02-0082", "12-02-01-0079", "12-03-03-0081", "12-01-02-0082", "12-21-02-0082", "12-03-02-0080", "12-03-03-0080", "12-02-01-0078", "12-01-02-0083", "12-21-02-0083", "12-03-03-0082", "12-03-03-0083", "12-03-03-0065", "12-03-03-0067", "12-01-03-0078", "12-21-03-0078", "12-02-03-0057", "12-02-03-0058", "12-99-03-0078", "12-99-03-0079", "12-99-03-0032", "12-99-03-0058", "12-99-03-0011", "12-99-03-0059", "12-99-03-0062", "12-99-03-0004", "12-99-03-0010", "12-99-03-0127", "12-99-03-0065", "12-99-03-0067", "12-99-03-0074", "12-99-03-0075", "12-99-03-0076", "12-99-03-0077", "12-02-03-0056", "12-03-03-0086", "12-03-03-0026", "12-03-03-0087", "12-03-02-0081", "12-03-03-0088", "12-03-03-0089", "12-01-02-0084", "12-21-02-0084", "12-02-02-0081", "12-02-01-0083", "12-02-02-0083", "12-02-03-0083", "12-02-01-0084", "12-02-02-0084", "12-02-03-0084", "12-02-01-0085", "12-02-02-0085", "12-02-03-0085", "12-03-03-0094", "12-03-03-0095", "12-05-02-0133", "12-99-05-0075", "12-99-05-0074", "12-01-03-0085", "12-21-03-0085", "12-02-03-0086", "12-02-03-0087", "12-03-03-0090", "12-03-03-0091", "12-03-03-0093", "12-03-03-0092", "12-01-01-0074", "12-01-03-0074", "12-21-01-0074", "12-21-03-0074", "12-03-01-0010", "12-05-02-0040", "12-05-03-0023", "12-03-03-0100", "12-03-03-0101", "12-03-03-0102", "12-05-02-0136", "12-05-03-0136", "12-05-02-0137", "12-05-03-0137", "12-05-02-0138", "12-05-03-0138", "12-05-02-0139", "12-05-03-0139", "12-05-02-0140", "12-05-03-0140", "12-05-02-0141", "12-05-03-0141", "12-05-02-0142", "12-05-03-0142", "12-05-02-0143", "12-05-03-0143", "12-05-02-0144", "12-05-03-0144", "12-05-02-0145", "12-05-03-0145", "12-05-02-0146", "12-05-03-0146", "12-01-02-0090", "12-21-02-0090", "12-02-01-0066", "12-02-02-0066", "12-02-03-0066", "12-02-02-0090", "12-02-01-0089", "12-02-02-0089", "12-02-03-0089", "12-01-03-0090", "12-21-03-0090", "12-03-03-0103", "12-03-03-0104", "12-03-03-0105", "12-03-03-0106", "12-02-02-0099", "12-02-01-0091", "12-02-02-0091", "12-02-03-0091", "12-02-01-0092", "12-02-02-0092", "12-02-03-0092", "12-02-01-0093", "12-02-02-0093", "12-02-03-0093", "12-02-01-0094", "12-02-02-0094", "12-02-03-0094", "12-02-01-0095", "12-02-02-0095", "12-02-03-0095", "12-02-01-0096", "12-02-02-0096", "12-02-03-0096", "12-02-01-0097", "12-02-02-0097", "12-02-03-0097", "12-02-02-0098", "12-05-02-0147", "12-05-02-0057", "12-05-02-0058", "12-05-02-0148", "12-05-02-0149", "12-05-02-0150", "12-02-01-0103", "12-02-01-0104", "12-02-02-0103", "12-02-02-0104", "12-02-03-0103", "12-02-03-0104", "12-05-03-0133", "12-05-03-106", "12-05-03-0057", "12-05-03-0058", "12-05-03-0148", "12-05-03-0149", "12-05-03-0150", "12-05-03-0106", "12-01-07-0001", "12-00-07-0000", "12-01-03-0092", "12-21-03-0092", "12-02-03-0105", "12-02-03-0106", "12-02-03-0107", "12-02-03-0108", "12-03-03-0108", "12-03-03-0109", "12-03-03-0110", "12-01-01-0088", "12-01-02-0088", "12-01-03-0088", "12-21-01-0088", "12-21-02-0088", "12-21-03-0088", "12-01-02-0091", "12-21-02-0091", "12-02-01-0088", "12-02-02-0088", "12-02-03-0088", "12-02-01-0102", "12-02-02-0102", "12-02-03-0102", "12-02-01-0101", "12-02-02-0101", "12-02-03-0101", "12-05-01-0134", "12-05-02-0134", "12-05-03-0134", "12-05-02-0135", "12-05-03-0135", "12-05-02-0151", "12-05-03-0151", "12-03-03-0107", "12-05-01-0152", "12-05-02-0152", "12-05-03-0152", "12-06-03-0001", "12-06-03-0002", "12-06-03-0003", "12-06-03-0004", "12-06-03-0005", "12-06-03-0006", "12-02-01-0109", "12-02-02-0109", "12-02-03-0109", "12-02-01-0110", "12-02-02-0110", "12-02-03-0110", "12-02-01-0111", "12-02-02-0111", "12-02-03-0111", "12-02-01-0112", "12-02-02-0112", "12-02-03-0112", "12-02-01-0113", "12-02-02-0113", "12-02-03-0113", "12-02-01-0114", "12-02-02-0114", "12-02-01-0115", "12-02-02-0115", "12-02-01-0116", "12-02-02-0116", "12-02-01-0117", "12-02-02-0117", "12-02-01-0118", "12-02-02-0118", "12-01-01-0093", "12-01-02-0093", "12-21-01-0093", "12-21-02-0093", "12-01-01-0094", "12-01-02-0094", "12-21-02-0094", "12-21-01-0094", "12-01-01-0095", "12-01-02-0095", "12-21-01-0095", "12-21-02-0095", "12-01-01-0096", "12-01-02-0096", "12-21-01-0096", "12-21-02-0096", "12-01-01-0097", "12-01-02-0097", "12-21-01-0097", "12-21-02-0097", "12-01-01-0098", "12-01-02-0098", "12-21-01-0098", "12-21-02-0098", "12-01-01-0068", "12-21-01-0068", "12-99-05-0077", "12-05-02-0160", "12-99-05-0076", "12-05-03-0160", "12-01-02-0099", "12-21-02-0099", "12-03-02-0111", "12-03-02-0112", "12-03-03-0111", "12-03-03-0112", "12-05-02-0161", "12-05-03-0161", "12-01-02-0103", "12-21-02-0103", "12-01-02-0104", "12-21-02-0104", "12-01-02-0105", "12-21-02-0105", "12-01-02-0106", "12-21-02-0106", "12-01-02-0107", "12-21-02-0107", "12-01-02-0108", "12-21-02-0108", "12-01-02-0109", "12-21-02-0109", "12-01-02-0110", "12-21-02-0110", "12-02-02-0119", "12-02-02-0120", "12-02-02-0121", "12-03-02-0113", "12-05-02-0162", "12-05-02-0163", "12-05-02-0164", "12-05-02-0165", "12-01-02-0100", "12-21-02-0100", "12-01-02-0101", "12-21-02-0101", "12-01-02-0102", "12-21-02-0102", "12-02-03-0119", "12-02-03-0120", "12-02-03-0121", "12-03-03-0113", "12-05-03-0162", "12-05-03-0163", "12-05-03-0164", "12-05-03-0165", "12-01-02-0111", "12-21-02-0111", "12-01-02-0112", "12-21-02-0112", "12-02-02-0122", "12-02-02-0123", "12-05-02-0166", "12-05-02-0167", "12-01-03-0112", "12-21-03-0112", "12-02-03-0122", "12-02-03-0123", "12-03-03-0114", "12-05-03-0166", "12-05-03-0167", "12-03-02-0115", "12-03-03-0115", "12-02-02-0125", "12-02-03-0126", "12-02-02-0127", "12-02-03-0128", "12-02-02-0129", "12-02-03-0130", "12-05-02-0169", "12-05-02-0170", "12-05-02-0171", "12-05-02-0172", "12-05-02-0173", "12-05-02-174", "12-05-02-0174", "12-03-03-0116", "12-05-02-0177", "12-05-02-0178", "12-05-02-0179", "12-05-02-0180", "12-05-02-0181", "12-05-02-0182", "12-05-02-0183", "12-05-02-0184", "12-05-03-0178", "12-05-03-0179", "12-05-03-0180", "12-05-03-0181", "12-05-03-0182", "12-05-03-0183", "12-05-03-0184", "12-05-03-0185", "12-02-01-0132", "12-02-02-0132", "12-05-02-0188", "12-05-02-0189", "12-05-02-0190", "12-05-02-0191", "12-01-02-0116", "12-21-02-0116", "12-03-03-0122", "12-03-03-0123", "12-01-01-0117", "12-01-02-0117", "12-21-01-0117", "12-21-02-0117", "12-02-01-0133", "12-02-02-0133", "12-02-01-0134", "12-02-02-0134", "12-02-01-0135", "12-02-02-0135", "12-03-03-0124", "12-03-03-0125", "12-03-03-0126", "12-05-01-0192", "12-05-02-0192", "12-05-03-0192", "12-05-02-0193", "12-05-03-0193", "11-99-05-0060", "11-99-05-0061", "11-99-05-0062", "11-99-05-0063", "11-99-05-0064", "11-99-05-0065", "12-99-02-0064", "12-99-02-0065", "12-99-03-0080", "12-99-02-0066", "12-99-02-0067", "12-99-03-0081", "12-99-03-0082", "12-03-01-0129", "12-01-02-0092", "12-21-02-0092", "12-03-03-0047", "12-03-02-0047", "12-03-01-0130", "12-03-02-0130", "12-03-03-0130", "12-03-01-0131", "12-03-02-0131", "12-03-03-0131", "12-03-01-0047", "12-04-02-0059", "12-04-02-0060", "12-04-02-0061", "12-04-02-0062", "12-04-02-0063", "12-04-02-0064", "12-04-02-0065", "12-04-02-0066", "12-04-02-0067", "12-04-02-0068", "12-04-02-0069", "12-04-02-0070", "12-04-02-0071", "12-04-02-0072", "12-04-02-0073", "12-04-02-0074", "12-02-02-0148", "12-02-02-0149", "12-02-02-0150", "12-02-02-0151", "12-02-02-0152", "12-02-02-0153", "12-02-02-0154", "12-02-02-0155", "12-02-02-0156", "12-02-02-0157", "12-02-02-0158", "12-03-03-0132", "12-03-03-0133", "12-01-03-0119", "12-21-03-0119", "12-03-03-0134", "12-02-03-0159", "12-02-03-0160", "12-02-03-0161", "12-03-03-0135", "12-03-03-0136", "12-03-03-0137", "12-03-03-0138", "12-03-03-0139", "12-03-03-0140", "12-03-03-0141", "12-03-03-0142", "12-03-03-0143", "12-03-03-0144", "12-03-03-0145", "12-99-02-0062", "12-99-02-0133", "12-99-02-0160", "12-99-02-0094", "12-99-02-0177", "12-99-02-0178", "12-99-02-0179", "12-99-02-0180", "12-99-02-0181", "12-99-02-0182", "12-99-02-0183", "12-99-02-0184", "12-99-02-0106", "12-99-02-0114", "12-99-02-0104", "12-99-02-0161", "12-99-02-0136", "12-99-03-0103", "12-99-02-0010", "12-99-02-0134", "12-99-02-0192", "12-99-03-0104", "12-99-03-0105", "12-99-02-0111", "12-99-02-0070", "12-99-02-0069", "12-99-02-0191", "12-99-02-0188", "12-99-02-0040", "12-99-02-0193", "12-99-02-0004", "12-99-02-0195", "12-99-02-0196", "12-99-02-0197", "12-99-02-0198", "12-99-02-0199", "12-99-02-0200", "12-99-03-0186", "12-99-03-0187", "12-99-03-0188"] diff --git a/src/lib/workers/androidRecipeExport.worker.ts b/src/lib/workers/androidRecipeExport.worker.ts new file mode 100644 index 0000000..9727d6e --- /dev/null +++ b/src/lib/workers/androidRecipeExport.worker.ts @@ -0,0 +1,121 @@ +type AndroidRecipeExportRow = { + lineNumber: number; + cells: string[]; + values: Record; +}; + +type AndroidRecipeExportData = { + headers: string[]; + rows: AndroidRecipeExportRow[]; + lineCount: number; +}; + +type ParseRequest = { + id: number; + raw: string; + maxRows: number; +}; + +function normalizeHeader(value: string, index: number): string { + const header = value.trim().replace(/^\uFEFF/, ''); + return header || `Column ${index + 1}`; +} + +function splitTsvLine(line: string): string[] { + return line.split('\t').map((cell) => cell.trim()); +} + +function collectNonEmptyLines(raw: string, maxLines: number): string[] { + const lines: string[] = []; + let lineStart = 0; + + for (let index = 0; index <= raw.length; index += 1) { + const isEnd = index === raw.length; + const char = raw[index]; + + if (!isEnd && char !== '\n') continue; + + const lineEnd = index > lineStart && raw[index - 1] === '\r' ? index - 1 : index; + const line = raw.slice(lineStart, lineEnd); + + if (line.trim().length > 0) { + lines.push(line); + if (lines.length >= maxLines) break; + } + + lineStart = index + 1; + } + + return lines; +} + +function buildUniqueHeaders(rawHeaders: string[], maxColumns: number): string[] { + const headers = [...rawHeaders]; + + for (let i = headers.length; i < maxColumns; i += 1) { + headers.push(`Column ${i + 1}`); + } + + const seen = new Map(); + return headers.map((header, index) => { + const normalized = normalizeHeader(header, index); + const count = seen.get(normalized) ?? 0; + seen.set(normalized, count + 1); + + return count === 0 ? normalized : `${normalized} ${count + 1}`; + }); +} + +function parseAndroidRecipeExport(raw: string, maxRows: number): AndroidRecipeExportData { + const maxLines = Number.isFinite(maxRows) ? Math.max(1, maxRows + 1) : Number.MAX_SAFE_INTEGER; + const lines = collectNonEmptyLines(raw, maxLines); + + if (lines.length === 0) { + return { + headers: [], + rows: [], + lineCount: 0 + }; + } + + const parsedLines = lines.map(splitTsvLine); + const maxColumns = Math.max(...parsedLines.map((line) => line.length)); + const headers = buildUniqueHeaders(parsedLines[0], maxColumns); + + const rows = parsedLines.slice(1).map((cells, index) => { + const paddedCells = [...cells]; + + for (let cellIndex = paddedCells.length; cellIndex < headers.length; cellIndex += 1) { + paddedCells.push(''); + } + + const values = Object.fromEntries( + headers.map((header, cellIndex) => [header, paddedCells[cellIndex] ?? '']) + ); + + return { + lineNumber: index + 2, + cells: paddedCells, + values + }; + }); + + return { + headers, + rows, + lineCount: lines.length + }; +} + +self.onmessage = (event: MessageEvent) => { + const { id, raw, maxRows } = event.data; + + try { + const parsed = parseAndroidRecipeExport(raw, maxRows); + self.postMessage({ id, parsed }); + } catch (error: any) { + self.postMessage({ id, error: error?.message ?? 'Unable to parse recipe export.' }); + } +}; + +export {}; diff --git a/src/routes/(authed)/+layout.svelte b/src/routes/(authed)/+layout.svelte index 5bf2f28..34b2e1b 100644 --- a/src/routes/(authed)/+layout.svelte +++ b/src/routes/(authed)/+layout.svelte @@ -7,20 +7,38 @@ import '../layout.css'; import ErrorLayout from '$lib/components/error-layout.svelte'; import { sidebarStore } from '$lib/core/stores/sidebar'; - import { onMount } from 'svelte'; import { auth } from '$lib/core/stores/auth'; - import { get } from 'svelte/store'; import { connectToWebsocket } from '$lib/core/stores/websocketStore'; import * as adb from '$lib/core/adb/adb'; import { addNotification } from '$lib/core/stores/noti'; - import { AdbDaemonWebUsbDeviceManager } from '@yume-chan/adb-daemon-webusb'; + import { page } from '$app/stores'; + import { + AdbDaemonWebUsbDevice, + AdbDaemonWebUsbDeviceManager + } from '@yume-chan/adb-daemon-webusb'; import AdbWebCredentialStore from '@yume-chan/adb-credential-web'; import { deviceCredentialManager } from '$lib/core/adb/deviceCredManager'; let { children } = $props(); + let websocketConnectedForUid = $state(''); + let adbReconnectTriedForUid = $state(''); + + function getAutoConnectChannel(pathname: string) { + if (pathname.startsWith('/tools/create-menu')) { + return 'recipe'; + } + + if (pathname.startsWith('/tools/brew')) { + return 'brew'; + } + + return 'adb'; + } async function tryAutoConnect() { try { + if (adb.getAdbInstance()) return true; + if (!('usb' in navigator) || !AdbDaemonWebUsbDeviceManager.BROWSER) { throw new Error('WebUSB not supported, try using fallback or different browser'); } @@ -38,7 +56,12 @@ const credStore = new AdbWebCredentialStore(); try { - await adb.connectDeviceByCred(device, credStore); + const channel = getAutoConnectChannel($page.url.pathname); + if (channel === 'recipe') { + await adb.connectRecipeMenuDeviceByCred(device, credStore); + } else { + await adb.connectDeviceByCred(device, credStore, channel === 'brew'); + } return true; } catch (e: any) { if (e.message === 'CREDENTIAL_EXPIRED') { @@ -61,24 +84,28 @@ } } - onMount(async () => { - let currentUser = get(auth); - // console.log(`on mount layout current user: ${JSON.stringify(currentUser)}`); - if (currentUser) { - // console.log('id', await currentUser.getIdToken()); - - console.log('connect ws on mount'); - connectToWebsocket(await currentUser.getIdToken()); - await tryAutoConnect(); - } - }); - $effect(() => { - console.log('connect ws on effect'); + const currentUser = $auth; - setTimeout(async () => { - connectToWebsocket(await get(auth)?.getIdToken()); - }, 100); + if (!currentUser) { + websocketConnectedForUid = ''; + adbReconnectTriedForUid = ''; + return; + } + + if (websocketConnectedForUid !== currentUser.uid) { + websocketConnectedForUid = currentUser.uid; + console.log('connect ws after auth ready'); + + void currentUser.getIdToken().then((idToken) => { + connectToWebsocket(idToken); + }); + } + + if (adbReconnectTriedForUid !== currentUser.uid && !adb.getAdbInstance()) { + adbReconnectTriedForUid = currentUser.uid; + void tryAutoConnect(); + } }); @@ -94,7 +121,7 @@ }} > -
+
{@render children()}
diff --git a/src/routes/(authed)/admin/+layout.svelte b/src/routes/(authed)/admin/+layout.svelte index 3730cf4..ba25fe8 100644 --- a/src/routes/(authed)/admin/+layout.svelte +++ b/src/routes/(authed)/admin/+layout.svelte @@ -72,10 +72,14 @@

Admin Panel

-

Manage users, roles, and system settings

+

Manage users, roles, and system settings

- + diff --git a/src/routes/(authed)/departments/+page.svelte b/src/routes/(authed)/departments/+page.svelte index 869544a..e2e80f6 100644 --- a/src/routes/(authed)/departments/+page.svelte +++ b/src/routes/(authed)/departments/+page.svelte @@ -26,7 +26,7 @@ departmentStore.set(cnt); if (refPage === 'sheet') { - await goto('/sheet/overview'); + await goto(`/sheet/overview/${cnt}`); } else { await goto('/recipe/overview'); } diff --git a/src/routes/(authed)/sheet/add/[country]/[catalog]/+page.svelte b/src/routes/(authed)/sheet/add/[country]/[catalog]/+page.svelte new file mode 100644 index 0000000..3a88e2f --- /dev/null +++ b/src/routes/(authed)/sheet/add/[country]/[catalog]/+page.svelte @@ -0,0 +1,846 @@ + + + +
+ +
+
+
+ +
+

+ Add Menu: {getCatalogDisplayName(catalog)} +

+

+ {country.toUpperCase()} • {catalog} +

+
+
+ +
+ 10 ? 'default' : 'destructive'}> + Lock: {lockTimeout}s + + + + +
+
+
+ + +
+
+ + + + +
+ 1 +
+ Basic Information +
+ Enter menu name and description +
+ +
+
+ + +
+ {#if langConfig.secondary} +
+ + +
+ {/if} +
+
+
+ + +
+ {#if langConfig.secondary} +
+ + +
+ {/if} +
+
+
+ + + + + +
+ 2 +
+ Product Codes +
+ + Click on each field to select category and generate code + {#if existingCodeSet.size > 0} + ({existingCodeSet.size} existing codes) + {/if} + +
+ +
+ +
+ + + {:else} + Click to add... + + {/if} + +
+ + +
+ + + {:else} + Click to add... + + {/if} + +
+ + +
+ + + {:else} + Click to add... + + {/if} + +
+
+ + + +
+
+ + + + + +
+ 3 +
+ Image & Categories +
+ Set image filename and menu categories +
+ +
+
+ + +
+
+ + +
+
+
+ + + +
+
+
+ + +
+ + +
+
+
+
+ + + + + + + Select {tempLabels[codePopupType]} Product Code + + + Choose from existing product codes (Server, Draft, or Machine) + + + + +
+
+ + +
+ +
+ + +
+ {#if loadingCodes} +
+ + Loading codes... +
+ {:else if filteredCodes().length === 0} +
+ {#if codeSearchQuery} + No codes found matching "{codeSearchQuery}" + {:else} + No {tempLabels[codePopupType].toLowerCase()} product codes available + {/if} +
+ {:else} + {#each filteredCodes() as item} + + {/each} + {/if} +
+ +
+
+ {filteredCodes().length} code{filteredCodes().length !== 1 ? 's' : ''} available +
+ +
+
+
diff --git a/src/routes/(authed)/sheet/edit/[country]/[catalog]/+page.svelte b/src/routes/(authed)/sheet/edit/[country]/[catalog]/+page.svelte new file mode 100644 index 0000000..d8faf31 --- /dev/null +++ b/src/routes/(authed)/sheet/edit/[country]/[catalog]/+page.svelte @@ -0,0 +1,2656 @@ + + + +
+ +
+
+
+ +
+

+ Edit: {getCatalogDisplayName(catalog)} +

+

+ {country.toUpperCase()} • Primary language: {getCountryPrimaryLanguage(country)} • {catalog} +

+
+
+ +
+ + 10 ? 'default' : 'destructive'}> + Lock: {lockTimeout}s + + + + + + + + + + +
+
+
+ + +
+ {#if loading} +
+ +

Loading menu items...

+ {#if streamMeta} +
+ +

+ Chunk {streamMeta.current_chunk} / {streamMeta.total_chunks} + ({streamMeta.total_items} items) +

+
+ {/if} +
+ {:else if isSwapMode} +
+
+
+

Swap Menu Positions

+ +
+
+ + +
+
+ +
+ {#each swapItems as item, index (item.row_index)} +
handleSwapDragStart(event, index)} + ondragover={handleSwapDragOver} + ondrop={(event) => handleSwapDrop(event, index)} + ondragend={handleSwapDragEnd} + class:opacity-50={draggingSwapIndex === index} + > + + +
+ + + + +
+ +
+ {#if item.image_url} + {#key `${item.row_index}-${item.image_name ?? item.image_url}`} + + {/key} + {:else} +
+ No image +
+ {/if} +
+ +
+

+ {formatSheetText(item.primary_name || item.name_en, 'Untitled menu')} +

+ {#if item.secondary_name} +

+ {formatSheetText(item.secondary_name)} +

+ {/if} +
+
+
+
+ {/each} +
+
+ {:else if isEditMode && editingItem} + +
+ + +
+
+ {#if editingItem.image_url} + {#key `${editingItem.row_index}-${editingItem.image_name ?? editingItem.image_url}`} + + {/key} + {:else} +
+ No image +
+ {/if} +
+ +
+
+

+ Item #{editingItem.row_index} +

+

+ {formatSheetText( + editingItem.primary_name || editingItem.name_en, + 'Untitled menu' + )} +

+ {#if editingItem.secondary_name} +

+ {formatSheetText(editingItem.secondary_name)} +

+ {/if} + {#if editingItem.image_name} +

+ {formatSheetText(editingItem.image_name)} +

+ {/if} +
+ + {#if editingItem.product_codes && editingItem.product_codes.length > 0} +
+ {#each editingItem.product_codes as code} + + {formatSheetText(code)} + + {/each} +
+ {/if} + + + {#if getItemPrices(editingItem).hot || getItemPrices(editingItem).cold || getItemPrices(editingItem).blend} + {@const displayPrices = getItemPrices(editingItem)} +
+
+ {#if displayPrices.hot} + Hot {displayPrices.hot} + {/if} + {#if displayPrices.cold} + Cold {displayPrices.cold} + {/if} + {#if displayPrices.blend} + Blend {displayPrices.blend} + {/if} +
+ +
+ {:else if editingItem.product_codes?.length > 0} + + {/if} + +
+
+ + +
+
+ + +
+
+
+ +
+ + +
+
+
+
+ + {#if getVisibleSections(editingItem.new_layout_v2).length > 0} + + + Menu Data + + + {#each getVisibleSections(editingItem.new_layout_v2) as section} +
+
+

{getSectionTitle(section)}

+ Row {section.row_index} +
+
+ {#each getVisibleCells(section) as cell} +
+ + +
+ {/each} +
+
+ {/each} +
+
+ {/if} + + {#if getVisibleSections(editingItem.name_desc_v2).length > 0} + + + Translations + + + {#each getVisibleSections(editingItem.name_desc_v2) as section} +
+
+

{section.key}

+ Row {section.row_index} +
+
+ {#each getVisibleCells(section) as cell} +
+ + +
+ {/each} +
+
+ {/each} +
+
+ {/if} +
+ {:else} + +
+ {#each visibleMenuItems as item, index (item.row_index)} + + + + {#if newlyAddedRowIndices.has(item.row_index)} + NEW + {/if} + +
+ + +
+ +
+ {#if item.image_url} + {#key `${item.row_index}-${item.image_name ?? item.image_url}`} + + {/key} + {:else} +
+ No image +
+ {/if} +
+ +
+ +

+ {formatSheetText(item.primary_name || item.name_en, 'Untitled menu')} +

+ {#if item.secondary_name} +

+ {formatSheetText(item.secondary_name)} +

+ {/if} + +
+ + + {@const prices = getItemPrices(item)} + {#if prices.hot || prices.cold || prices.blend} +
+ {#if prices.hot} + Hot {prices.hot} + {/if} + {#if prices.cold} + Cold {prices.cold} + {/if} + {#if prices.blend} + Blend {prices.blend} + {/if} +
+ {/if} + + {#if item.product_codes && item.product_codes.length > 0} +
+
+ {#each item.product_codes.slice(0, 4) as code} + + {formatSheetText(code)} + + {/each} + {#if item.product_codes.length > 4} + + +{item.product_codes.length - 4} + + {/if} +
+
+ {/if} +
+
+ {/each} +
+ {/if} +
+
+ + + + + + + + Delete Menu + + + Are you sure you want to delete this menu item? This action cannot be undone. + + + + {#if itemToDelete} +
+

+ {formatSheetText(itemToDelete.primary_name || itemToDelete.name_en, 'Untitled')} +

+ {#if itemToDelete.secondary_name} +

+ {formatSheetText(itemToDelete.secondary_name)} +

+ {/if} +

Item #{itemToDelete.row_index}

+
+ {/if} + +
+ + +
+
+
+ + + + +
+
+ + + + Preview Online Menus + + + + +
+ {#each availablePreviewLanguageOptions as option} + + {/each} +
+
+ +
+ {#if previewLoading} +
+ +

Loading menus from machine...

+
+ {:else if previewMenus.length === 0} +
+

No online menus found

+
+ {:else if visiblePreviewMenus.length === 0} +
+

All menus have HIDE prices

+
+ {:else} +
+ {#each visiblePreviewMenus as menu (menu.row_index)} + {@const prices = getMenuPrices(menu)} + + + +
+ {#if menu.image_url} + {#key `${menu.productCode}-${menu.image_name ?? menu.image_url}`} + + {/key} + {:else} +
+ No image +
+ {/if} +
+ + +
+

+ {getPreviewMenuName(menu)} +

+ {#if getPreviewMenuDescription(menu)} +

+ {getPreviewMenuDescription(menu)} +

+ {/if} +
+ + +
+
+
+ {getVariantLabel('hot')} + {prices.hot} +
+
+ {getVariantLabel('cold')} + {prices.cold} +
+
+ {getVariantLabel('blend')} + {prices.blend} +
+
+
+
+ + {getVariantProductCode(menu, 'hot') || '—'} + +
+
+ + {getVariantProductCode(menu, 'cold') || '—'} + +
+
+ + {getVariantProductCode(menu, 'blend') || '—'} + +
+
+
+
+
+ {/each} +
+ {/if} +
+ + +
+
+
+

Gen Layout

+

+ Generate and push layout menu files to Machine +

+
+
+ + + {#if genLayoutStatus === 'complete' && genLayoutFiles.length > 0} + + {/if} + + +
+
+ + + {#if genLayoutStatus === 'receiving'} +
+ +

+ Receiving files: {$genLayoutBatch.files.length} / {$genLayoutBatch.total_files} +

+
+ {/if} + + {#if pushingToAndroid} +
+ 0 ? (pushProgress.current / pushProgress.total) * 100 : 0} + max={100} + class="h-2" + /> +

+ Pushing to Android: {pushProgress.current} / {pushProgress.total} +

+
+ {/if} + + + {#if genLayoutStatus === 'complete' && genLayoutFiles.length > 0} +
+

+ Generated {genLayoutFiles.length} files: +

+
+ {#each genLayoutFiles as file} +
+ {file.file.split('/').pop()} +
+ {/each} +
+
+ {/if} + + {#if genLayoutStatus === 'error'} +
+

{$genLayoutBatch.error}

+
+ {/if} +
+ + + + +
+
+
+ + + + + + + + Restart XMLEngine App + + + + +
+

+ {#if restartDialogSource === 'push-complete'} + Layout files were pushed successfully. Restart XMLEngine app to apply changes. + {:else} + The app will shut down and relaunch immediately. + {/if} +

+
+ +
+ + +
+
+
+ + + + + + Edit Prices + + + +
+ {#each priceEditData as item, index} +
+
+
+ + {item.type} + + {item.code} + {#if item.isNew} + NO PRICE + {/if} +
+
+
+ {getCurrencySymbol()} + +
+
+ {/each} + + {#if priceEditData.length === 0} +
+ No product codes found +
+ {/if} +
+ +
+ + +
+
+
diff --git a/src/routes/(authed)/sheet/overview/+page.server.ts b/src/routes/(authed)/sheet/overview/+page.server.ts new file mode 100644 index 0000000..baad7b2 --- /dev/null +++ b/src/routes/(authed)/sheet/overview/+page.server.ts @@ -0,0 +1,11 @@ +import { redirect } from '@sveltejs/kit'; +import { referenceFromPage } from '$lib/core/stores/recipeStore'; +import { get } from 'svelte/store'; + +export function load() { + // Set reference so departments page knows to redirect to sheet + referenceFromPage.set('sheet'); + + // Redirect to departments page to select country + throw redirect(302, '/departments'); +} diff --git a/src/routes/(authed)/sheet/overview/+page.svelte b/src/routes/(authed)/sheet/overview/+page.svelte index 7afa9d2..36c27e5 100644 --- a/src/routes/(authed)/sheet/overview/+page.svelte +++ b/src/routes/(authed)/sheet/overview/+page.svelte @@ -1,66 +1,149 @@
-
+
-

Layout overview [ {refDepartment} ]

+

Sheet Overview

- Display menus from the spreadsheet current selected country + Catalogs for {selectedCountry.toUpperCase()}

-
- +
+
- - - - - - + +
+ {#if loading} +
+ +

+ Loading catalogs for {selectedCountry}... +

+
+ {:else if error} +
+

{error}

+ +
+ {:else if catalogs.length === 0} +
+

No catalogs found for {selectedCountry}

+
+ {:else} + + + + Catalog Name + Status + Locked By + Actions + + + + {#each catalogs as catalog} + + {catalog.catalog} + + + {catalog.status.toUpperCase()} + + + + {catalog.locked_by || '-'} + + + + + + {/each} + + + {/if} +
diff --git a/src/routes/(authed)/sheet/overview/[country]/+page.svelte b/src/routes/(authed)/sheet/overview/[country]/+page.svelte new file mode 100644 index 0000000..6da4a55 --- /dev/null +++ b/src/routes/(authed)/sheet/overview/[country]/+page.svelte @@ -0,0 +1,279 @@ + + +
+
+
+
+

+ Sheet Overview [ {selectedCountry.toUpperCase()} ] +

+

+ View available catalogs for {selectedCountry.toUpperCase()} +

+
+ +
+ +
+ {#if enabledCountries.length === 0} +

+ No countries available. Please check your permissions. +

+ {:else} + { + if (v) { + selectedCountry = v; + goto(`/sheet/overview/${v}`); + } + }} + > + + {selectedCountry.toUpperCase()} + + + {#each enabledCountries as country} + + {country.toUpperCase()} + + {/each} + + + {/if} +
+ +
+ {#if loading} +
+ +

+ Loading catalogs for {selectedCountry}... +

+
+ {:else if error} +
+

{error}

+ +
+ {:else if sortedCatalogs.length === 0} +
+

No catalogs found for {selectedCountry}

+
+ {:else} +
+ {#each sortedCatalogs as catalog} + + +
+ + {getCatalogDisplayName(catalog.catalog)} group + + {#if isMainCatalog(catalog.catalog)} + + + Main + + {/if} +
+ +
+ +
+ {#if catalog.status === 'free'} + + + Available + + + + Ready to edit + + {:else} + + + Locked + + + {catalog.locked_by || 'In use'} + + {/if} +
+
+ + + +
+ {/each} +
+ {/if} +
+
+
diff --git a/src/routes/(authed)/tools/adv-upload/+page.svelte b/src/routes/(authed)/tools/adv-upload/+page.svelte new file mode 100644 index 0000000..ec63447 --- /dev/null +++ b/src/routes/(authed)/tools/adv-upload/+page.svelte @@ -0,0 +1,833 @@ + + +
+ +
+
+
+

Adv Upload

+

Upload advertisement videos (.mp4) to the server

+
+ +
+ + {isAdbConnected ? 'Machine connected' : 'Machine offline'} + + + {#if files.length > 0} + + {/if} + + + + + + +
+
+
+ + +
+
+ + + + Upload Settings + + +
+ + + + {COUNTRIES.find((c) => c.value === selectedCountry)?.label || 'Select country'} + + + {#each COUNTRIES as country} + {country.label} + {/each} + + + +
+ +
+
+ + + + + Naming & Size Rules + + +
+
+

taobin_adv_menu_*.mp4

+

Menu banner ad

+ 1080 × 380 +
+
+

taobin_adv_*.mp4

+

Fullscreen / Idle ad

+ 1080 × 608 +
+
+
+
+ + + + + + + + + + {#if pushingToMachine} +
+ 0 ? (pushProgress.current / pushProgress.total) * 100 : 0} + max={100} + class="h-2" + /> +

+ + Sending to machine ({pushProgress.current}/{pushProgress.total}): {pushProgress.name} +

+
+ {/if} + + + {#if uploading} +
+ 0 + ? (uploadProgress.current / uploadProgress.total) * 100 + : 0} + max={100} + class="h-2" + /> +

+ Uploading: {uploadProgress.current} / {uploadProgress.total} +

+
+ {/if} + + + {#if generatingManifest} +

+ + Generating sync_1.file on machine and uploading... +

+ {/if} + + + {#if files.length > 0} + + + + Selected Files ({files.length}) +
+ {#if files.some((f) => f.status === 'success')} + + {files.filter((f) => f.status === 'success').length} uploaded + + {/if} + {#if files.some((f) => f.status === 'error')} + + {files.filter((f) => f.status === 'error').length} invalid/failed + + {/if} +
+
+
+ +
+ {#each files as item (item.id)} +
+ +
+ + + + {#if item.status === 'uploading'} +
+ +
+ {:else if item.status === 'success'} +
+ +
+ {/if} + + + {#if item.status !== 'uploading'} + + {/if} +
+ + +
+

+ {item.file.name} +

+
+ + {item.category === 'menu' ? 'Menu banner' : 'Fullscreen'} + + {#if !item.dimChecked} + Reading… + {:else if item.dimOk} + + {item.width}×{item.height} + + {:else} + + {item.width ?? '?'}×{item.height ?? '?'} + + {/if} + + {(item.file.size / (1024 * 1024)).toFixed(1)} MB + +
+

+ Expected {item.expectedWidth}×{item.expectedHeight} +

+ {#if item.error} +

+ + {item.error} +

+ {/if} +
+
+ {/each} +
+
+
+ {/if} +
+
+
diff --git a/src/routes/(authed)/tools/android-recipe/+page.svelte b/src/routes/(authed)/tools/android-recipe/+page.svelte new file mode 100644 index 0000000..e2f198c --- /dev/null +++ b/src/routes/(authed)/tools/android-recipe/+page.svelte @@ -0,0 +1,42 @@ + + +{#if AndroidRecipeExportView} + +{:else} +
+
+

Android Recipe Export

+

Preparing recipe export viewer.

+
+ +
+ {#if loadError} +

Unable to load viewer

+

{loadError}

+ {:else} +
+ +
+

Processing

+

Loading Android recipe tools...

+
+
+ {/if} +
+
+{/if} diff --git a/src/routes/(authed)/tools/brew/+page.svelte b/src/routes/(authed)/tools/brew/+page.svelte index 2dfaf9e..eddda99 100644 --- a/src/routes/(authed)/tools/brew/+page.svelte +++ b/src/routes/(authed)/tools/brew/+page.svelte @@ -1,7 +1,8 @@ + +
+
+

Create Menu

+

Create and manage menu drafts for Android

+
+ + +
+ {#if !isAdbConnected} + + {:else if !isRecipeLoaded} + + {#if !isAndroidSocketConnected} + + {/if} + {:else} + + + {/if} + + + {#if isAdbConnected} +
+ {#if countryLoading} + Loading country... + {:else if detectedCountry} + + Country: {detectedCountry} ({detectedCountryCode}) + + {/if} +
+ {/if} +
+ + + {#if stagedMenus.length > 0} +
+
+
+

Draft Menus

+

+ Menus created on web. Save to Android when ready. +

+
+ +
+
+ {#each stagedMenus as menu (menu.productCode)} + {@const saveState = $menuSaveStates.get(menu.productCode)} + {@const isSaving = saveState?.status === 'saving'} + {@const isSaved = saveState?.status === 'saved'} + {@const isError = saveState?.status === 'error'} + {@const isVerifying = pendingSaveVerification.has(menu.productCode)} + {@const isProcessing = isSaving || isVerifying} +
+
+
+
{menu.productCode}
+ {#if isSaving} +
+ + Saving... +
+ {:else if isVerifying} +
+ + Verifying... +
+ {:else if isSaved} +
Saved
+ {:else if isError} +
+ {saveState?.error ?? 'Save failed'} +
+ {/if} +
+
{menu.name || menu.otherName}
+
+ {getActiveRecipeStepCount(menu)} recipe steps - {getActiveToppingSetCount(menu)} + topping groups +
+
+
+ + + + +
+
+ {/each} +
+
+ {:else} +
+ No draft menus yet. Click "Create New Menu" to get started. +
+ {/if} +
+ + + + + + Create New Menu + Select temperature types and category + + +
+ + {#if detectedCountry} +
+
Machine Country
+
{detectedCountry} (prefix: {detectedCountryCode})
+
+ {/if} + + +
+ +
+ {#each ['hot', 'cold', 'blend'] as temp} + + {/each} +
+
+ + +
+ +
+ {#each categoryOptions as cat} + + {/each} +
+
+ + + {#if selectedTempCount > 0 && selectedCategory} +
+ +
+ {#if generatedCodes.hot}
Hot: {generatedCodes.hot}
{/if} + {#if generatedCodes.cold}
Cold: {generatedCodes.cold}
{/if} + {#if generatedCodes.blend}
Blend: {generatedCodes.blend}
{/if} +
+
+ {/if} +
+ + + + + +
+
+ + + + + + {editingDraftProductCode ? 'Edit Draft Menu' : 'Create Menu'} + + {editingDraftProductCode + ? 'Update the draft menu.' + : `Creating ${tempForms.length} menu(s). Menu info is separate for each temperature.`} + + + +
+ + {#if tempForms.length > 1} +
+ {#each tempForms as form, index} + + {/each} +
+ {/if} + + + {#if activeForm} +
+
+

+ Menu Info ({activeForm.temp.charAt(0).toUpperCase() + activeForm.temp.slice(1)}) +

+
+
+ + updateActiveFormField('name', event.currentTarget.value)} + /> +
+
+ + updateActiveFormField('otherName', event.currentTarget.value)} + /> +
+
+
+
+ + + updateActiveFormField('description', event.currentTarget.value)} + /> +
+
+ + + updateActiveFormField('otherDescription', event.currentTarget.value)} + /> +
+
+
+
+ + updateActiveFormField('cashPrice', event.currentTarget.value)} + /> +
+
+ + + updateActiveFormField('nonCashPrice', event.currentTarget.value)} + /> +
+
+ + updateActiveFormField('image', event.currentTarget.value)} + /> +
+
+ +
+ + +
+ + +
+ + +
+
+
+

Materials

+

Recipe steps for {activeForm.temp}

+
+ +
+ + {#if activeMaterials.length === 0} +
+ No active materials found. Load recipe data from Android first. +
+ {:else} +
+ {#each activeForm.recipeSteps as step, index} +
+
+
Step {index + 1}
+ {#if activeForm.recipeSteps.length > 1} + + {/if} +
+
+
+ + +
+
+
+ + + updateRecipeStepNumber(index, 'powderGram', e.currentTarget.value)} + /> +
+
+ + + updateRecipeStepNumber(index, 'powderTime', e.currentTarget.value)} + /> +
+
+ + + updateRecipeStepNumber(index, 'syrupGram', e.currentTarget.value)} + /> +
+
+ + + updateRecipeStepNumber(index, 'syrupTime', e.currentTarget.value)} + /> +
+
+ + + updateRecipeStepNumber(index, 'waterYield', e.currentTarget.value)} + /> +
+
+ + + updateRecipeStepNumber(index, 'waterCold', e.currentTarget.value)} + /> +
+
+ + + updateRecipeStepNumber(index, 'stirTime', e.currentTarget.value)} + /> +
+
+ + + updateRecipeStepNumber(index, 'MixOrder', e.currentTarget.value)} + /> +
+
+ + + updateRecipeStepNumber(index, 'FeedParameter', e.currentTarget.value)} + /> +
+
+ + + updateRecipeStepNumber(index, 'FeedPattern', e.currentTarget.value)} + /> +
+
+
+
+ {/each} +
+ {/if} +
+ + +
+
+
+

Toppings

+

Toppings for {activeForm.temp}

+
+ +
+ + {#if activeToppingGroups.length === 0} +
+ No topping data found. +
+ {:else if activeForm.toppingOptions.length === 0} +
+ No toppings added yet. +
+ {:else} +
+ {#each activeForm.toppingOptions as topping, index} +
+
+
Topping {index + 1}
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ {/each} +
+ {/if} +
+
+ {/if} +
+ + + + + +
+
+ + + + + + Confirm Brew + Review the recipe before brewing + + + {#if pendingBrewMenu} +
+
+
Product Code
+
{pendingBrewMenu.productCode}
+
+ +
+
Name
+
+ {pendingBrewMenu.name || pendingBrewMenu.otherName} +
+
+ + {#if getPendingBrewMaterials().length > 0} +
+
Materials
+
+ {#each getPendingBrewMaterials() as mat} +
+ {mat.name} + + {mat.powderGram}g / {mat.waterYield}ml + +
+ {/each} +
+
+ {/if} + + {#if getPendingBrewToppings().length > 0} +
+
Toppings
+
+ {#each getPendingBrewToppings() as topping} +
+
{topping.group}
+
{topping.topping}
+
+ {/each} +
+
+ {/if} +
+ {/if} + + + + + +
+
diff --git a/src/routes/(authed)/tools/image-upload/+page.svelte b/src/routes/(authed)/tools/image-upload/+page.svelte new file mode 100644 index 0000000..757eb84 --- /dev/null +++ b/src/routes/(authed)/tools/image-upload/+page.svelte @@ -0,0 +1,442 @@ + + +
+ +
+
+
+

Image Upload

+

Upload menu images to the server

+
+ +
+ {#if files.length > 0} + + {/if} + +
+
+
+ + +
+
+ + + + Upload Settings + + +
+
+ + + + {COUNTRIES.find((c) => c.value === selectedCountry)?.label || 'Select country'} + + + {#each COUNTRIES as country} + {country.label} + {/each} + + +
+ +
+ + + + {ALLOWED_FOLDERS.find((f) => f.value === selectedFolder)?.label || 'Select folder'} + + + {#each ALLOWED_FOLDERS as folder} + {folder.label} + {/each} + + +
+
+ +
+ +
+
+
+ + + + + + + + + + {#if uploading} +
+ 0 + ? (uploadProgress.current / uploadProgress.total) * 100 + : 0} + max={100} + class="h-2" + /> +

+ Uploading: {uploadProgress.current} / {uploadProgress.total} +

+
+ {/if} + + + {#if files.length > 0} + + + + Selected Files ({files.length}) +
+ {#if files.some((f) => f.status === 'success')} + + {files.filter((f) => f.status === 'success').length} uploaded + + {/if} + {#if files.some((f) => f.status === 'error')} + + {files.filter((f) => f.status === 'error').length} failed + + {/if} + {#if files.some((f) => f.status === 'pending')} + + {files.filter((f) => f.status === 'pending').length} pending + + {/if} +
+
+
+ +
+ {#each files as item (item.id)} +
+ +
+ {item.file.name} +
+ + + {#if item.status === 'uploading'} +
+ +
+ {:else if item.status === 'success'} +
+ +
+ {:else if item.status === 'error'} +
+ +
+ {/if} + + + {#if item.status !== 'uploading'} + + {/if} + + +
+

+ {item.file.name} +

+

+ {(item.file.size / 1024).toFixed(1)} KB +

+ {#if item.error} +

+ {item.error} +

+ {/if} +
+
+ {/each} +
+
+
+ {/if} +
+
+
diff --git a/src/routes/api/adv-manifest/+server.ts b/src/routes/api/adv-manifest/+server.ts new file mode 100644 index 0000000..c532f1e --- /dev/null +++ b/src/routes/api/adv-manifest/+server.ts @@ -0,0 +1,42 @@ +import { json, error } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; +import { env } from '$env/dynamic/public'; + +// Method 2: forward a machine-generated sync_1.file to the adv FTP server. +const ADV_API_BASE = env.PUBLIC_POST_IMAGE; + +export const POST: RequestHandler = async ({ request }) => { + try { + const formData = await request.formData(); + + const country = formData.get('country') as string; + const uid = formData.get('uid') as string; + const displayName = formData.get('displayName') as string; + const email = formData.get('email') as string; + const file = formData.get('file') as File; + + if (!country || !uid || !displayName || !email || !file) { + throw error(400, 'Missing required fields'); + } + + const endpoint = `${ADV_API_BASE}/adv/manifest/${encodeURIComponent(country)}/${encodeURIComponent(uid)}/${encodeURIComponent(displayName)}/${encodeURIComponent(email)}`; + + const uploadFormData = new FormData(); + uploadFormData.append('file', file); + + const response = await fetch(endpoint, { method: 'POST', body: uploadFormData }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({ detail: response.statusText })); + throw error(response.status, errorData.detail || 'Manifest upload failed'); + } + + return json(await response.json()); + } catch (err) { + console.error('[Adv Manifest Proxy] Error:', err); + if (err && typeof err === 'object' && 'status' in err) { + throw err; + } + throw error(500, err instanceof Error ? err.message : 'Internal server error'); + } +}; diff --git a/src/routes/api/adv-upload/+server.ts b/src/routes/api/adv-upload/+server.ts new file mode 100644 index 0000000..877a12d --- /dev/null +++ b/src/routes/api/adv-upload/+server.ts @@ -0,0 +1,55 @@ +import { json, error } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; +import { env } from '$env/dynamic/public'; + +// Adv videos are served by the same taobin_image service as menu images. +const ADV_API_BASE = env.PUBLIC_POST_IMAGE; + +export const POST: RequestHandler = async ({ request }) => { + try { + const formData = await request.formData(); + + const country = formData.get('country') as string; + const uid = formData.get('uid') as string; + const displayName = formData.get('displayName') as string; + const email = formData.get('email') as string; + const file = formData.get('file') as File; + // 'false' when the manifest is generated on a machine (method 2). + const regenerate = (formData.get('regenerate') as string) ?? 'true'; + + if (!country || !uid || !displayName || !email || !file) { + throw error(400, 'Missing required fields'); + } + + const endpoint = + `${ADV_API_BASE}/adv/upload/${encodeURIComponent(country)}/${encodeURIComponent(uid)}/${encodeURIComponent(displayName)}/${encodeURIComponent(email)}` + + `?regenerate=${encodeURIComponent(regenerate)}`; + + console.log('[Adv Upload Proxy] Endpoint:', endpoint, 'file:', file.name); + + // Upstream expects the multipart field name `files`. + const uploadFormData = new FormData(); + uploadFormData.append('files', file); + + const response = await fetch(endpoint, { + method: 'POST', + body: uploadFormData + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({ detail: response.statusText })); + throw error(response.status, errorData.detail || 'Upload failed'); + } + + const result = await response.json(); + return json(result); + } catch (err) { + console.error('[Adv Upload Proxy] Error:', err); + + if (err && typeof err === 'object' && 'status' in err) { + throw err; + } + + throw error(500, err instanceof Error ? err.message : 'Internal server error'); + } +}; diff --git a/src/routes/api/image-upload/+server.ts b/src/routes/api/image-upload/+server.ts new file mode 100644 index 0000000..aabd5bb --- /dev/null +++ b/src/routes/api/image-upload/+server.ts @@ -0,0 +1,58 @@ +import { json, error } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; +import { env } from '$env/dynamic/public'; + +const IMAGE_API_BASE = env.PUBLIC_POST_IMAGE; + +export const POST: RequestHandler = async ({ request }) => { + try { + const formData = await request.formData(); + + const country = formData.get('country') as string; + const folder = formData.get('folder') as string; + const uid = formData.get('uid') as string; + const displayName = formData.get('displayName') as string; + const email = formData.get('email') as string; + const file = formData.get('file') as File; + + if (!folder || !uid || !displayName || !email || !file) { + throw error(400, 'Missing required fields'); + } + + + // Build the upload endpoint + let endpoint: string; + if (country) { + endpoint = `${IMAGE_API_BASE}/inter/${encodeURIComponent(country)}/image/${encodeURIComponent(folder)}/upload/${encodeURIComponent(uid)}/${encodeURIComponent(displayName)}/${encodeURIComponent(email)}`; + } else { + endpoint = `${IMAGE_API_BASE}/image/${encodeURIComponent(folder)}/upload/${encodeURIComponent(uid)}/${encodeURIComponent(displayName)}/${encodeURIComponent(email)}`; + } + + console.log('[Image Upload Proxy] Endpoint:', endpoint); + + // Create new FormData for the upstream request + const uploadFormData = new FormData(); + uploadFormData.append('files', file); + + const response = await fetch(endpoint, { + method: 'POST', + body: uploadFormData + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({ detail: response.statusText })); + throw error(response.status, errorData.detail || 'Upload failed'); + } + + const result = await response.json(); + return json(result); + } catch (err) { + console.error('[Image Upload Proxy] Error:', err); + + if (err && typeof err === 'object' && 'status' in err) { + throw err; + } + + throw error(500, err instanceof Error ? err.message : 'Internal server error'); + } +}; diff --git a/src/routes/api/sheet/stream/+server.ts b/src/routes/api/sheet/stream/+server.ts new file mode 100644 index 0000000..5ecd79c --- /dev/null +++ b/src/routes/api/sheet/stream/+server.ts @@ -0,0 +1,91 @@ +import { json } from '@sveltejs/kit'; + +// In-memory store for streamed catalog data +// Format: { batchId: { chunks: [...], status: 'collecting'|'complete'|'error' } } +const streamCache = new Map(); + +export async function POST({ request }) { + try { + const data = await request.json(); + const { batch_id, msg, content, current_chunk, total_chunks } = data.payload; + + // Initialize or update batch + if (!streamCache.has(batch_id)) { + streamCache.set(batch_id, { + chunks: [], + status: 'collecting', + total_chunks, + createdAt: Date.now() + }); + } + + const batch = streamCache.get(batch_id); + + // Handle different message types + if (msg === 'start') { + console.log(`[API] Stream started for batch ${batch_id}`); + } else if (msg === 'chunk') { + batch.chunks.push(content); + console.log(`[API] Received chunk ${current_chunk}/${total_chunks} for batch ${batch_id}`); + } else if (msg === 'end') { + batch.status = 'complete'; + console.log(`[API] Stream complete for batch ${batch_id}, total chunks: ${batch.chunks.length}`); + } else if (msg === 'error') { + batch.status = 'error'; + batch.error = content; + console.log(`[API] Stream error for batch ${batch_id}:`, content); + } + + return json({ status: 'received', batch_id }); + } catch (error) { + console.error('[API] Error processing stream:', error); + return json({ status: 'error', message: String(error) }, { status: 500 }); + } +} + +export function GET({ url }) { + const batchId = url.searchParams.get('batch_id'); + + // Clean up old cache entries (older than 5 minutes) + const now = Date.now(); + for (const [key, value] of streamCache.entries()) { + if (now - value.createdAt > 5 * 60 * 1000) { + streamCache.delete(key); + } + } + + // If batch_id specified, return that specific batch + if (batchId) { + const batch = streamCache.get(batchId); + + if (!batch) { + return json({ status: 'not_found' }, { status: 404 }); + } + + return json({ + batch_id: batchId, + status: batch.status, + chunks: batch.chunks, + total_chunks: batch.total_chunks, + error: batch.error || null, + createdAt: new Date(batch.createdAt).toISOString() + }); + } + + // Otherwise return list of all recent batches + const batches = Array.from(streamCache.entries()).map(([batchId, batch]) => ({ + batch_id: batchId, + status: batch.status, + chunks_count: batch.chunks.length, + total_chunks: batch.total_chunks, + error: batch.error || null, + createdAt: new Date(batch.createdAt).toISOString() + })); + + return json({ + status: 'success', + batches: batches.sort( + (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() + ) + }); +} diff --git a/vite.config.ts b/vite.config.ts index aa4d2d9..0b49238 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -21,7 +21,7 @@ export default defineConfig({ environment: 'browser', browser: { enabled: true, - provider: 'playwright', + //provider: 'playwright', instances: [{ browser: 'chromium' }] }, include: ['src/**/*.svelte.{test,spec}.{js,ts}'],