From 7679f138b6e2cf52e15296e8fcd52b3f123cd257 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sat, 27 Sep 2025 14:27:24 +0200 Subject: [PATCH] chore: init --- .gitignore | 34 ++ Dockerfile | 38 ++ README.md | 15 + bun.lock | 46 ++ index.ts | 129 ++++++ iprooms.csv | 2 + package.json | 15 + redis.ts | 102 +++++ tsconfig.json | 28 ++ web/.gitignore | 23 + web/.npmrc | 1 + web/Dockerfile | 16 + web/README.md | 38 ++ web/bun.lock | 398 ++++++++++++++++++ web/components.json | 16 + web/package.json | 35 ++ web/src/app.css | 121 ++++++ web/src/app.d.ts | 13 + web/src/app.html | 11 + web/src/lib/AppSidebar.svelte | 171 ++++++++ web/src/lib/ConnectionLostDialog.svelte | 24 ++ web/src/lib/Spinner.svelte | 110 +++++ web/src/lib/assets/favicon.svg | 1 + .../alert-dialog/alert-dialog-action.svelte | 18 + .../alert-dialog/alert-dialog-cancel.svelte | 18 + .../alert-dialog/alert-dialog-content.svelte | 27 ++ .../alert-dialog-description.svelte | 17 + .../alert-dialog/alert-dialog-footer.svelte | 20 + .../alert-dialog/alert-dialog-header.svelte | 20 + .../alert-dialog/alert-dialog-overlay.svelte | 20 + .../ui/alert-dialog/alert-dialog-title.svelte | 17 + .../alert-dialog/alert-dialog-trigger.svelte | 7 + .../lib/components/ui/alert-dialog/index.ts | 39 ++ web/src/lib/components/ui/badge/badge.svelte | 50 +++ web/src/lib/components/ui/badge/index.ts | 2 + .../ui/breadcrumb/breadcrumb-ellipsis.svelte | 23 + .../ui/breadcrumb/breadcrumb-item.svelte | 20 + .../ui/breadcrumb/breadcrumb-link.svelte | 31 ++ .../ui/breadcrumb/breadcrumb-list.svelte | 23 + .../ui/breadcrumb/breadcrumb-page.svelte | 23 + .../ui/breadcrumb/breadcrumb-separator.svelte | 27 ++ .../ui/breadcrumb/breadcrumb.svelte | 21 + web/src/lib/components/ui/breadcrumb/index.ts | 25 ++ .../lib/components/ui/button/button.svelte | 80 ++++ web/src/lib/components/ui/button/index.ts | 17 + .../lib/components/ui/card/card-action.svelte | 20 + .../components/ui/card/card-content.svelte | 15 + .../ui/card/card-description.svelte | 20 + .../lib/components/ui/card/card-footer.svelte | 20 + .../lib/components/ui/card/card-header.svelte | 23 + .../lib/components/ui/card/card-title.svelte | 20 + web/src/lib/components/ui/card/card.svelte | 23 + web/src/lib/components/ui/card/index.ts | 25 ++ .../components/ui/dialog/dialog-close.svelte | 7 + .../ui/dialog/dialog-content.svelte | 43 ++ .../ui/dialog/dialog-description.svelte | 17 + .../components/ui/dialog/dialog-footer.svelte | 20 + .../components/ui/dialog/dialog-header.svelte | 20 + .../ui/dialog/dialog-overlay.svelte | 20 + .../components/ui/dialog/dialog-title.svelte | 17 + .../ui/dialog/dialog-trigger.svelte | 7 + web/src/lib/components/ui/dialog/index.ts | 37 ++ .../dropdown-menu-checkbox-item.svelte | 41 ++ .../dropdown-menu-content.svelte | 27 ++ .../dropdown-menu-group-heading.svelte | 22 + .../dropdown-menu/dropdown-menu-group.svelte | 7 + .../dropdown-menu/dropdown-menu-item.svelte | 27 ++ .../dropdown-menu/dropdown-menu-label.svelte | 24 ++ .../dropdown-menu-radio-group.svelte | 16 + .../dropdown-menu-radio-item.svelte | 31 ++ .../dropdown-menu-separator.svelte | 17 + .../dropdown-menu-shortcut.svelte | 20 + .../dropdown-menu-sub-content.svelte | 20 + .../dropdown-menu-sub-trigger.svelte | 29 ++ .../dropdown-menu-trigger.svelte | 7 + .../lib/components/ui/dropdown-menu/index.ts | 49 +++ web/src/lib/components/ui/input-otp/index.ts | 15 + .../ui/input-otp/input-otp-group.svelte | 20 + .../ui/input-otp/input-otp-separator.svelte | 19 + .../ui/input-otp/input-otp-slot.svelte | 31 ++ .../components/ui/input-otp/input-otp.svelte | 22 + web/src/lib/components/ui/input/index.ts | 7 + web/src/lib/components/ui/input/input.svelte | 51 +++ web/src/lib/components/ui/label/index.ts | 7 + web/src/lib/components/ui/label/label.svelte | 20 + web/src/lib/components/ui/separator/index.ts | 7 + .../components/ui/separator/separator.svelte | 20 + web/src/lib/components/ui/sheet/index.ts | 36 ++ .../components/ui/sheet/sheet-close.svelte | 7 + .../components/ui/sheet/sheet-content.svelte | 58 +++ .../ui/sheet/sheet-description.svelte | 17 + .../components/ui/sheet/sheet-footer.svelte | 20 + .../components/ui/sheet/sheet-header.svelte | 20 + .../components/ui/sheet/sheet-overlay.svelte | 20 + .../components/ui/sheet/sheet-title.svelte | 17 + .../components/ui/sheet/sheet-trigger.svelte | 7 + .../lib/components/ui/sidebar/constants.ts | 6 + .../components/ui/sidebar/context.svelte.ts | 81 ++++ web/src/lib/components/ui/sidebar/index.ts | 75 ++++ .../ui/sidebar/sidebar-content.svelte | 24 ++ .../ui/sidebar/sidebar-footer.svelte | 21 + .../ui/sidebar/sidebar-group-action.svelte | 36 ++ .../ui/sidebar/sidebar-group-content.svelte | 21 + .../ui/sidebar/sidebar-group-label.svelte | 34 ++ .../ui/sidebar/sidebar-group.svelte | 21 + .../ui/sidebar/sidebar-header.svelte | 21 + .../ui/sidebar/sidebar-input.svelte | 21 + .../ui/sidebar/sidebar-inset.svelte | 24 ++ .../ui/sidebar/sidebar-menu-action.svelte | 43 ++ .../ui/sidebar/sidebar-menu-badge.svelte | 29 ++ .../ui/sidebar/sidebar-menu-button.svelte | 103 +++++ .../ui/sidebar/sidebar-menu-item.svelte | 21 + .../ui/sidebar/sidebar-menu-skeleton.svelte | 36 ++ .../ui/sidebar/sidebar-menu-sub-button.svelte | 43 ++ .../ui/sidebar/sidebar-menu-sub-item.svelte | 21 + .../ui/sidebar/sidebar-menu-sub.svelte | 25 ++ .../components/ui/sidebar/sidebar-menu.svelte | 21 + .../ui/sidebar/sidebar-provider.svelte | 53 +++ .../components/ui/sidebar/sidebar-rail.svelte | 36 ++ .../ui/sidebar/sidebar-separator.svelte | 19 + .../ui/sidebar/sidebar-trigger.svelte | 35 ++ .../lib/components/ui/sidebar/sidebar.svelte | 104 +++++ web/src/lib/components/ui/skeleton/index.ts | 7 + .../components/ui/skeleton/skeleton.svelte | 17 + web/src/lib/components/ui/sonner/index.ts | 1 + .../lib/components/ui/sonner/sonner.svelte | 13 + web/src/lib/components/ui/table/index.ts | 28 ++ .../lib/components/ui/table/table-body.svelte | 20 + .../components/ui/table/table-caption.svelte | 20 + .../lib/components/ui/table/table-cell.svelte | 23 + .../components/ui/table/table-footer.svelte | 20 + .../lib/components/ui/table/table-head.svelte | 23 + .../components/ui/table/table-header.svelte | 20 + .../lib/components/ui/table/table-row.svelte | 23 + web/src/lib/components/ui/table/table.svelte | 22 + web/src/lib/components/ui/tooltip/index.ts | 21 + .../ui/tooltip/tooltip-content.svelte | 47 +++ .../ui/tooltip/tooltip-trigger.svelte | 7 + web/src/lib/hooks/is-mobile.svelte.ts | 9 + web/src/lib/index.ts | 1 + web/src/lib/search-form.svelte | 21 + web/src/lib/switcher.svelte | 45 ++ web/src/lib/utils.ts | 13 + web/src/lib/ws.svelte.ts | 57 +++ web/src/routes/(manage)/+layout.svelte | 92 ++++ web/src/routes/(manage)/+page.svelte | 43 ++ web/src/routes/(manage)/admin/+page.svelte | 1 + web/src/routes/(manage)/entry/+page.svelte | 120 ++++++ web/src/routes/(manage)/log/+page.svelte | 68 +++ .../routes/(manage)/room/[slug]/+page.svelte | 135 ++++++ .../(manage)/ticket/[ticket]/+page.svelte | 100 +++++ web/src/routes/display/+layout.svelte | 49 +++ web/src/routes/display/+page.svelte | 66 +++ web/static/robots.txt | 3 + web/svelte.config.js | 18 + web/tsconfig.json | 19 + web/vite.config.ts | 7 + 157 files changed, 5197 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 bun.lock create mode 100644 index.ts create mode 100644 iprooms.csv create mode 100644 package.json create mode 100644 redis.ts create mode 100644 tsconfig.json create mode 100644 web/.gitignore create mode 100644 web/.npmrc create mode 100644 web/Dockerfile create mode 100644 web/README.md create mode 100644 web/bun.lock create mode 100644 web/components.json create mode 100644 web/package.json create mode 100644 web/src/app.css create mode 100644 web/src/app.d.ts create mode 100644 web/src/app.html create mode 100644 web/src/lib/AppSidebar.svelte create mode 100644 web/src/lib/ConnectionLostDialog.svelte create mode 100644 web/src/lib/Spinner.svelte create mode 100644 web/src/lib/assets/favicon.svg create mode 100644 web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte create mode 100644 web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte create mode 100644 web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte create mode 100644 web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte create mode 100644 web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte create mode 100644 web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte create mode 100644 web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte create mode 100644 web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte create mode 100644 web/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte create mode 100644 web/src/lib/components/ui/alert-dialog/index.ts create mode 100644 web/src/lib/components/ui/badge/badge.svelte create mode 100644 web/src/lib/components/ui/badge/index.ts create mode 100644 web/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte create mode 100644 web/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte create mode 100644 web/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte create mode 100644 web/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte create mode 100644 web/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte create mode 100644 web/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte create mode 100644 web/src/lib/components/ui/breadcrumb/breadcrumb.svelte create mode 100644 web/src/lib/components/ui/breadcrumb/index.ts create mode 100644 web/src/lib/components/ui/button/button.svelte create mode 100644 web/src/lib/components/ui/button/index.ts create mode 100644 web/src/lib/components/ui/card/card-action.svelte create mode 100644 web/src/lib/components/ui/card/card-content.svelte create mode 100644 web/src/lib/components/ui/card/card-description.svelte create mode 100644 web/src/lib/components/ui/card/card-footer.svelte create mode 100644 web/src/lib/components/ui/card/card-header.svelte create mode 100644 web/src/lib/components/ui/card/card-title.svelte create mode 100644 web/src/lib/components/ui/card/card.svelte create mode 100644 web/src/lib/components/ui/card/index.ts create mode 100644 web/src/lib/components/ui/dialog/dialog-close.svelte create mode 100644 web/src/lib/components/ui/dialog/dialog-content.svelte create mode 100644 web/src/lib/components/ui/dialog/dialog-description.svelte create mode 100644 web/src/lib/components/ui/dialog/dialog-footer.svelte create mode 100644 web/src/lib/components/ui/dialog/dialog-header.svelte create mode 100644 web/src/lib/components/ui/dialog/dialog-overlay.svelte create mode 100644 web/src/lib/components/ui/dialog/dialog-title.svelte create mode 100644 web/src/lib/components/ui/dialog/dialog-trigger.svelte create mode 100644 web/src/lib/components/ui/dialog/index.ts create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte create mode 100644 web/src/lib/components/ui/dropdown-menu/index.ts create mode 100644 web/src/lib/components/ui/input-otp/index.ts create mode 100644 web/src/lib/components/ui/input-otp/input-otp-group.svelte create mode 100644 web/src/lib/components/ui/input-otp/input-otp-separator.svelte create mode 100644 web/src/lib/components/ui/input-otp/input-otp-slot.svelte create mode 100644 web/src/lib/components/ui/input-otp/input-otp.svelte create mode 100644 web/src/lib/components/ui/input/index.ts create mode 100644 web/src/lib/components/ui/input/input.svelte create mode 100644 web/src/lib/components/ui/label/index.ts create mode 100644 web/src/lib/components/ui/label/label.svelte create mode 100644 web/src/lib/components/ui/separator/index.ts create mode 100644 web/src/lib/components/ui/separator/separator.svelte create mode 100644 web/src/lib/components/ui/sheet/index.ts create mode 100644 web/src/lib/components/ui/sheet/sheet-close.svelte create mode 100644 web/src/lib/components/ui/sheet/sheet-content.svelte create mode 100644 web/src/lib/components/ui/sheet/sheet-description.svelte create mode 100644 web/src/lib/components/ui/sheet/sheet-footer.svelte create mode 100644 web/src/lib/components/ui/sheet/sheet-header.svelte create mode 100644 web/src/lib/components/ui/sheet/sheet-overlay.svelte create mode 100644 web/src/lib/components/ui/sheet/sheet-title.svelte create mode 100644 web/src/lib/components/ui/sheet/sheet-trigger.svelte create mode 100644 web/src/lib/components/ui/sidebar/constants.ts create mode 100644 web/src/lib/components/ui/sidebar/context.svelte.ts create mode 100644 web/src/lib/components/ui/sidebar/index.ts create mode 100644 web/src/lib/components/ui/sidebar/sidebar-content.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-footer.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-group-action.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-group-content.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-group-label.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-group.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-header.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-input.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-inset.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-menu-action.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-menu-button.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-menu-item.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-menu.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-provider.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-rail.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-separator.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar-trigger.svelte create mode 100644 web/src/lib/components/ui/sidebar/sidebar.svelte create mode 100644 web/src/lib/components/ui/skeleton/index.ts create mode 100644 web/src/lib/components/ui/skeleton/skeleton.svelte create mode 100644 web/src/lib/components/ui/sonner/index.ts create mode 100644 web/src/lib/components/ui/sonner/sonner.svelte create mode 100644 web/src/lib/components/ui/table/index.ts create mode 100644 web/src/lib/components/ui/table/table-body.svelte create mode 100644 web/src/lib/components/ui/table/table-caption.svelte create mode 100644 web/src/lib/components/ui/table/table-cell.svelte create mode 100644 web/src/lib/components/ui/table/table-footer.svelte create mode 100644 web/src/lib/components/ui/table/table-head.svelte create mode 100644 web/src/lib/components/ui/table/table-header.svelte create mode 100644 web/src/lib/components/ui/table/table-row.svelte create mode 100644 web/src/lib/components/ui/table/table.svelte create mode 100644 web/src/lib/components/ui/tooltip/index.ts create mode 100644 web/src/lib/components/ui/tooltip/tooltip-content.svelte create mode 100644 web/src/lib/components/ui/tooltip/tooltip-trigger.svelte create mode 100644 web/src/lib/hooks/is-mobile.svelte.ts create mode 100644 web/src/lib/index.ts create mode 100644 web/src/lib/search-form.svelte create mode 100644 web/src/lib/switcher.svelte create mode 100644 web/src/lib/utils.ts create mode 100644 web/src/lib/ws.svelte.ts create mode 100644 web/src/routes/(manage)/+layout.svelte create mode 100644 web/src/routes/(manage)/+page.svelte create mode 100644 web/src/routes/(manage)/admin/+page.svelte create mode 100644 web/src/routes/(manage)/entry/+page.svelte create mode 100644 web/src/routes/(manage)/log/+page.svelte create mode 100644 web/src/routes/(manage)/room/[slug]/+page.svelte create mode 100644 web/src/routes/(manage)/ticket/[ticket]/+page.svelte create mode 100644 web/src/routes/display/+layout.svelte create mode 100644 web/src/routes/display/+page.svelte create mode 100644 web/static/robots.txt create mode 100644 web/svelte.config.js create mode 100644 web/tsconfig.json create mode 100644 web/vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..da2993e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +# use the official Bun image +# see all versions at https://hub.docker.com/r/oven/bun/tags +FROM oven/bun:1 AS base +WORKDIR /usr/src/app + +# install dependencies into temp directory +# this will cache them and speed up future builds +FROM base AS install +RUN mkdir -p /temp/dev +COPY package.json bun.lock /temp/dev/ +RUN cd /temp/dev && bun install --frozen-lockfile + +# install with --production (exclude devDependencies) +RUN mkdir -p /temp/prod +COPY package.json bun.lock /temp/prod/ +RUN cd /temp/prod && bun install --frozen-lockfile --production + +# copy node_modules from temp directory +# then copy all (non-ignored) project files into the image +FROM base AS prerelease +COPY --from=install /temp/dev/node_modules node_modules +COPY . . + +# [optional] tests & build +ENV NODE_ENV=production +RUN bun test +RUN bun run build + +# copy production dependencies and source code into final image +FROM base AS release +COPY --from=install /temp/prod/node_modules node_modules +COPY --from=prerelease /usr/src/app/*.ts ./ +COPY --from=prerelease /usr/src/app/package.json . + +# run the app +USER bun +EXPOSE 3000/tcp +ENTRYPOINT [ "bun", "run", "index.ts" ] diff --git a/README.md b/README.md new file mode 100644 index 0000000..83b03f7 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# nextup + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.2.10. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..d1aee97 --- /dev/null +++ b/bun.lock @@ -0,0 +1,46 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "nextup", + "dependencies": { + "redis": "^5.8.2", + }, + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@redis/bloom": ["@redis/bloom@5.8.2", "", { "peerDependencies": { "@redis/client": "^5.8.2" } }, "sha512-855DR0ChetZLarblio5eM0yLwxA9Dqq50t8StXKp5bAtLT0G+rZ+eRzzqxl37sPqQKjUudSYypz55o6nNhbz0A=="], + + "@redis/client": ["@redis/client@5.8.2", "", { "dependencies": { "cluster-key-slot": "1.1.2" } }, "sha512-WtMScno3+eBpTac1Uav2zugXEoXqaU23YznwvFgkPwBQVwEHTDgOG7uEAObtZ/Nyn8SmAMbqkEubJaMOvnqdsQ=="], + + "@redis/json": ["@redis/json@5.8.2", "", { "peerDependencies": { "@redis/client": "^5.8.2" } }, "sha512-uxpVfas3I0LccBX9rIfDgJ0dBrUa3+0Gc8sEwmQQH0vHi7C1Rx1Qn8Nv1QWz5bohoeIXMICFZRcyDONvum2l/w=="], + + "@redis/search": ["@redis/search@5.8.2", "", { "peerDependencies": { "@redis/client": "^5.8.2" } }, "sha512-cNv7HlgayavCBXqPXgaS97DRPVWFznuzsAmmuemi2TMCx5scwLiP50TeZvUS06h/MG96YNPe6A0Zt57yayfxwA=="], + + "@redis/time-series": ["@redis/time-series@5.8.2", "", { "peerDependencies": { "@redis/client": "^5.8.2" } }, "sha512-g2NlHM07fK8H4k+613NBsk3y70R2JIM2dPMSkhIjl2Z17SYvaYKdusz85d7VYOrZBWtDrHV/WD2E3vGu+zni8A=="], + + "@types/bun": ["@types/bun@1.2.22", "", { "dependencies": { "bun-types": "1.2.22" } }, "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA=="], + + "@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="], + + "@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="], + + "bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="], + + "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "redis": ["redis@5.8.2", "", { "dependencies": { "@redis/bloom": "5.8.2", "@redis/client": "5.8.2", "@redis/json": "5.8.2", "@redis/search": "5.8.2", "@redis/time-series": "5.8.2" } }, "sha512-31vunZj07++Y1vcFGcnNWEf5jPoTkGARgfWI4+Tk55vdwHxhAvug8VEtW7Cx+/h47NuJTEg/JL77zAwC6E0OeA=="], + + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + + "undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="], + } +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..125e659 --- /dev/null +++ b/index.ts @@ -0,0 +1,129 @@ +import type { ServerWebSocket } from "bun"; +import { callTicket, completeTicket, getCurrentTicket, getDisplayTickets, getLogEntries, getTicket, noShowTicket } from "./redis"; + +let IP_TO_ROOMS: Record = {}; + +const iprooms = await Bun.file("./iprooms.csv").text(); +const lines = iprooms.split("\n"); +for (const line of lines.slice(1)) { + const [ip, room] = line.split(","); + if (ip && room) { + IP_TO_ROOMS[ip.trim()] = room.trim(); + } +} + +export let displaySockets: ServerWebSocket[] = []; + +export async function broadcastDisplayUpdate() { + for (const ws of displaySockets) { + if (ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ type: "display", tickets: await getDisplayTickets() })); + } + } +} + +Bun.serve({ + fetch(req, server) { + // upgrade the request to a WebSocket + if (server.upgrade(req)) { + return; // do not return a Response + } + return new Response("Upgrade failed", { status: 500 }); + }, + websocket: { + async message(ws, message) { + console.log("Received message:", message); + const data = JSON.parse(message.toString()); + if (data.type === "hello") {} + else if (data.type === "my-room") { + console.log("Client requested room for IP:", ws.remoteAddress); + const room = IP_TO_ROOMS[ws.remoteAddress]; + if (!room) { + return void ws.send(JSON.stringify({ type: "error", code: -1, message: "No room" })); + } + ws.send(JSON.stringify({ type: "my-room", room }) ); + // } else if (data.type === "create-ticket") { + // const ticket = await getTicket(data.ticket); + // if(!ticket || ticket.status == "completed" || ticket.status == "no-show") { + // await createTicket(data.ticket); + // ws.send(JSON.stringify({ type: "create-ticket", status: "created", ticket, num: data.ticket }) ); + // return; + // } + // ws.send(JSON.stringify({ type: "error", code: -2, message: "Ticket already exists and is not completed or no-show", ticket, num: data.ticket })); + } else if (data.type === "call-ticket") { + // const ticket = await getTicket(data.ticket); + // if(!ticket || ticket.status == "completed" || ticket.status == "no-show") { + // return void ws.send(JSON.stringify({ type: "error", code: -3, message: "Ticket does not exist or is completed or no-show", ticket, num: data.ticket, room: data.room }) ); + // } + await callTicket(data.ticket, data.room); + ws.send(JSON.stringify({ type: "call-ticket", status: "called", ticket: await getTicket(data.ticket), num: data.ticket, room: data.room }) ); + await broadcastDisplayUpdate(); + } else if (data.type === "complete-ticket") { + const ticket = await getTicket(data.ticket); + if(ticket?.status !== "called") { + await completeTicket(data.ticket); + await broadcastDisplayUpdate(); + return void ws.send(JSON.stringify({ type: "complete-ticket", status: "skipped", ticket, num: data.ticket }) ); + } + // // Check how long since called + // const calledDate = new Date(ticket.calledDate || "1970-01-01T00:00:00.000Z"); + // const now = new Date(); + // const diff = now.getTime() - calledDate.getTime(); + // if(diff < 5 * 60 * 1000) { // 5 minutes + // // Mark as no-show + // await noShowTicket(data.ticket); + // await broadcastDisplayUpdate(); + // return void ws.send(JSON.stringify({ type: "complete-ticket", status: "no-show", ticket, num: data.ticket })); + // } + + await completeTicket(data.ticket); + ws.send(JSON.stringify({ type: "complete-ticket", status: "completed", ticket, num: data.ticket })); + await broadcastDisplayUpdate(); + } else if (data.type === "no-show") { + const ticket = await getTicket(data.ticket); + if(!ticket) { + return; + } + // // Check how long since called + // const calledDate = new Date(ticket.calledDate || "1970-01-01T00:00:00.000Z"); + // const now = new Date(); + // const diff = now.getTime() - calledDate.getTime(); + // if(diff < 5 * 60 * 1000) { // 5 minutes + // // Mark as no-show + // await noShowTicket(data.ticket); + // await broadcastDisplayUpdate(); + // return void ws.send(JSON.stringify({ type: "complete-ticket", status: "no-show", ticket, num: data.ticket })); + // } + + await noShowTicket(data.ticket); + ws.send(JSON.stringify({ type: "complete-ticket", status: "no-show", ticket, num: data.ticket })); + await broadcastDisplayUpdate(); + } else if (data.type === "get-log") { + const log = await getLogEntries(); + ws.send(JSON.stringify({ type: "log", log })); + } else if (data.type === "get-ticket") { + const ticket = await getTicket(data.ticket); + ws.send(JSON.stringify({ type: "ticket", ticket, num: data.ticket })); + } else if (data.type === "get-current-ticket") { + const ticket = await getCurrentTicket(data.room); + ws.send(JSON.stringify({ type: "current-ticket", ...ticket, room: data.room })); + } else if (data.type == "display") { + if(!displaySockets.includes(ws)) { + displaySockets.push(ws); + console.log("Added display socket. Total:", displaySockets.length); + ws.send(JSON.stringify({ type: "display", tickets: await getDisplayTickets() })); + } + } + }, // a message is received + open(ws) { + console.log("WebSocket opened"); + ws.send(JSON.stringify({ type: "hello" })); + }, // a socket is opened + close(ws, code, message) { + console.log("WebSocket closed"); + }, // a socket is closed + drain(ws) { + + }, // the socket is ready to receive more data + } +}); \ No newline at end of file diff --git a/iprooms.csv b/iprooms.csv new file mode 100644 index 0000000..a1d4f35 --- /dev/null +++ b/iprooms.csv @@ -0,0 +1,2 @@ +IP,Rooms +::ffff:127.0.0.1,1 \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..22053dd --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "nextup", + "module": "index.ts", + "type": "module", + "private": true, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "redis": "^5.8.2" + } +} diff --git a/redis.ts b/redis.ts new file mode 100644 index 0000000..4d8c71e --- /dev/null +++ b/redis.ts @@ -0,0 +1,102 @@ +import { createClient } from "redis"; + +type LogAction = "called" | "completed" | "no-show"; +type TicketStatus = "called" | "completed" | "no-show"; + +interface LogEntry { + ticket: string; + time: string; + action: LogAction; + room: string | null; +}; + +interface Ticket { + status: TicketStatus; + created: string; + room: string | null; + calledDate: string | null; +} + +const redis = await createClient({ + url: process.env.DATABASE_URL || "redis://localhost:6379" +}).connect(); + +export function logAction(entry: LogEntry) { + redis.lPush("log", JSON.stringify(entry)); +} + +export async function getLogEntries(): Promise { + const entries = await redis.lRange("log", 0, -1); + return entries.map(entry => JSON.parse(entry)); +} + +// export async function createTicket(ticket: string) { +// // redisjson ticket:{ticket} = { status: "waiting", created: now, room: null, calledDate: null } +// await redis.json.set(`ticket:${ticket}`, "$", { +// status: "waiting" as TicketStatus, +// created: new Date().toISOString(), +// room: null, +// calledDate: null +// }); +// logAction({ ticket, time: new Date().toISOString(), action: "entered", room: null }); +// } + +export async function callTicket(ticket: string, room: string) { + // await redis.json.set(`ticket:${ticket}`, "$.status", "called"); + // await redis.json.set(`ticket:${ticket}`, "$.room", room); + // await redis.json.set(`ticket:${ticket}`, "$.calledDate", new Date().toISOString()); + await redis.json.set(`ticket:${ticket}`, "$", { + status: "called" as TicketStatus, + created: new Date().toISOString(), + room, + calledDate: new Date().toISOString() + }); + logAction({ ticket, time: new Date().toISOString(), action: "called", room }); +} + +export async function completeTicket(ticket: string) { + await redis.json.set(`ticket:${ticket}`, "$.status", "completed"); + const room = await redis.json.get(`ticket:${ticket}`, { path: "$.room" }) as string | null; + logAction({ ticket, time: new Date().toISOString(), action: "completed", room }); +} + +export async function noShowTicket(ticket: string) { + await redis.json.set(`ticket:${ticket}`, "$.status", "no-show"); + const room = await redis.json.get(`ticket:${ticket}`, { path: "$.room" }) as string | null; + logAction({ ticket, time: new Date().toISOString(), action: "no-show", room }); +} + +export async function getCurrentTicket(room: string): Promise<{num: string, ticket: Ticket} | null> { + const tickets = await redis.keys("ticket:*"); + for (const key of tickets) { + const ticket = key.split(":")[1]!; + const ticketData = await getTicket(ticket); + if (ticketData?.status === "called" && ticketData.room === room) { + return { num: ticket, ticket: ticketData }; + } + } + return null; +} + +export async function getDisplayTickets(): Promise<{num: string, ticket: Ticket}[]> { + const result: {num: string, ticket: Ticket}[] = []; + const tickets = await redis.keys("ticket:*"); + for (const key of tickets) { + const ticket = key.split(":")[1]!; + const ticketData = await getTicket(ticket); + if (ticketData?.status === "called" || ticketData?.status === "no-show") { + result.push({ num: ticket, ticket: ticketData }); + } + } + // Sort by calledDate + result.sort((a, b) => { + const dateA = new Date(a.ticket.calledDate || "1970-01-01T00:00:00.000Z"); + const dateB = new Date(b.ticket.calledDate || "1970-01-01T00:00:00.000Z"); + return dateA.getTime() - dateB.getTime(); + }); + return result; +} + +export async function getTicket(ticket: string): Promise { + return await redis.json.get(`ticket:${ticket}`) as Ticket | null; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9c62f74 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..3b462cb --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,23 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/web/.npmrc b/web/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/web/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/web/Dockerfile b/web/Dockerfile new file mode 100644 index 0000000..c1ae596 --- /dev/null +++ b/web/Dockerfile @@ -0,0 +1,16 @@ +FROM node:22-alpine AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build +RUN npm prune --production + +FROM node:22-alpine +WORKDIR /app +COPY --from=builder /app/build build/ +COPY --from=builder /app/node_modules node_modules/ +COPY package.json . +EXPOSE 3000 +ENV NODE_ENV=production +CMD [ "node", "build" ] \ No newline at end of file diff --git a/web/README.md b/web/README.md new file mode 100644 index 0000000..75842c4 --- /dev/null +++ b/web/README.md @@ -0,0 +1,38 @@ +# sv + +Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```sh +# create a new project in the current directory +npx sv create + +# create a new project in my-app +npx sv create my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```sh +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```sh +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/web/bun.lock b/web/bun.lock new file mode 100644 index 0000000..817271b --- /dev/null +++ b/web/bun.lock @@ -0,0 +1,398 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "web", + "devDependencies": { + "@internationalized/date": "^3.8.1", + "@lucide/svelte": "^0.544.0", + "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/adapter-node": "^5.3.2", + "@sveltejs/kit": "^2.22.0", + "@sveltejs/vite-plugin-svelte": "^6.0.0", + "@tailwindcss/vite": "^4.0.0", + "bits-ui": "^2.11.0", + "clsx": "^2.1.1", + "mode-watcher": "^1.1.0", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "svelte-sonner": "^1.0.5", + "tailwind-merge": "^3.3.1", + "tailwind-variants": "^3.1.1", + "tailwindcss": "^4.0.0", + "tw-animate-css": "^1.4.0", + "typescript": "^5.0.0", + "vite": "^7.0.4", + }, + }, + }, + "packages": { + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.10", "", { "os": "android", "cpu": "arm" }, "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.10", "", { "os": "android", "cpu": "arm64" }, "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.10", "", { "os": "android", "cpu": "x64" }, "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.10", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.10", "", { "os": "linux", "cpu": "arm" }, "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.10", "", { "os": "linux", "cpu": "ia32" }, "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.10", "", { "os": "none", "cpu": "x64" }, "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.10", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.10", "", { "os": "openbsd", "cpu": "x64" }, "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.10", "", { "os": "sunos", "cpu": "x64" }, "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + + "@internationalized/date": ["@internationalized/date@3.9.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-yaN3brAnHRD+4KyyOsJyk49XUvj2wtbNACSqg0bz3u8t2VuzhC8Q5dfRnrSxjnnbDb+ienBnkn1TzQfE154vyg=="], + + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + + "@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.544.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-9f9O6uxng2pLB01sxNySHduJN3HTl5p0HDu4H26VR51vhZfiMzyOMe9Mhof3XAk4l813eTtl+/DYRvGyoRR+yw=="], + + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], + + "@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@28.0.6", "", { "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-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw=="], + + "@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.1", "", { "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-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA=="], + + "@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.52.2", "", { "os": "android", "cpu": "arm" }, "sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.2", "", { "os": "android", "cpu": "arm64" }, "sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.2", "", { "os": "linux", "cpu": "arm" }, "sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.2", "", { "os": "linux", "cpu": "none" }, "sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.2", "", { "os": "linux", "cpu": "none" }, "sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.2", "", { "os": "linux", "cpu": "none" }, "sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.2", "", { "os": "linux", "cpu": "x64" }, "sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.2", "", { "os": "linux", "cpu": "x64" }, "sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.2", "", { "os": "none", "cpu": "arm64" }, "sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.2", "", { "os": "win32", "cpu": "x64" }, "sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.2", "", { "os": "win32", "cpu": "x64" }, "sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="], + + "@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@6.1.0", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-shOuLI5D2s+0zTv2ab5M5PqfknXqWbKi+0UwB9yLTRIdzsK1R93JOO8jNhIYSHdW+IYXIYnLniu+JZqXs7h9Wg=="], + + "@sveltejs/adapter-node": ["@sveltejs/adapter-node@5.3.2", "", { "dependencies": { "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.0", "rollup": "^4.9.5" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0" } }, "sha512-nBJSipMb1KLjnAM7uzb+YpnA1VWKb+WdR+0mXEnXI6K1A3XYWbjkcjnW20ubg07sicK8XaGY/FAX3PItw39qBQ=="], + + "@sveltejs/kit": ["@sveltejs/kit@2.43.4", "", { "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.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.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", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-GfvOq3A/qMRhj2L9eKjxaI8FLqZDh5SY74YzhRKT//u2AvQw96ksEfjuHviC4jg9U08mBVB0Y47EwEJHO4BB4Q=="], + + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", "deepmerge": "^4.3.1", "magic-string": "^0.30.17", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ=="], + + "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.1", "", { "dependencies": { "debug": "^4.4.1" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA=="], + + "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.13", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.5.1", "lightningcss": "1.30.1", "magic-string": "^0.30.18", "source-map-js": "^1.2.1", "tailwindcss": "4.1.13" } }, "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.13", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.13", "@tailwindcss/oxide-darwin-arm64": "4.1.13", "@tailwindcss/oxide-darwin-x64": "4.1.13", "@tailwindcss/oxide-freebsd-x64": "4.1.13", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", "@tailwindcss/oxide-linux-x64-musl": "4.1.13", "@tailwindcss/oxide-wasm32-wasi": "4.1.13", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" } }, "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.13", "", { "os": "android", "cpu": "arm64" }, "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.13", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13", "", { "os": "linux", "cpu": "arm" }, "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.13", "", { "os": "linux", "cpu": "x64" }, "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.13", "", { "os": "linux", "cpu": "x64" }, "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.13", "", { "dependencies": { "@emnapi/core": "^1.4.5", "@emnapi/runtime": "^1.4.5", "@emnapi/wasi-threads": "^1.0.4", "@napi-rs/wasm-runtime": "^0.2.12", "@tybys/wasm-util": "^0.10.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.13", "", { "os": "win32", "cpu": "x64" }, "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.13", "", { "dependencies": { "@tailwindcss/node": "4.1.13", "@tailwindcss/oxide": "4.1.13", "tailwindcss": "4.1.13" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ=="], + + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "bits-ui": ["bits-ui@2.11.3", "", { "dependencies": { "@floating-ui/core": "^1.7.1", "@floating-ui/dom": "^1.7.1", "esm-env": "^1.1.2", "runed": "^0.31.1", "svelte-toolbelt": "^0.10.4", "tabbable": "^6.2.0" }, "peerDependencies": { "@internationalized/date": "^3.8.1", "svelte": "^5.33.0" } }, "sha512-XhTuOgcgFatw2qxd+hIlu3EqfYu9R2FFMeJg8srZo3UVKBdOVWHbT0L3GBH46XsaESkTz3aEjU+BiGVoI/2rnQ=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="], + + "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "detect-libc": ["detect-libc@2.1.1", "", {}, "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw=="], + + "devalue": ["devalue@5.3.2", "", {}, "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], + + "esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], + + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], + + "esrap": ["esrap@2.1.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA=="], + + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "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=="], + + "jiti": ["jiti@2.6.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ=="], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], + + "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], + + "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + + "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=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + + "rollup": ["rollup@4.52.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.2", "@rollup/rollup-android-arm64": "4.52.2", "@rollup/rollup-darwin-arm64": "4.52.2", "@rollup/rollup-darwin-x64": "4.52.2", "@rollup/rollup-freebsd-arm64": "4.52.2", "@rollup/rollup-freebsd-x64": "4.52.2", "@rollup/rollup-linux-arm-gnueabihf": "4.52.2", "@rollup/rollup-linux-arm-musleabihf": "4.52.2", "@rollup/rollup-linux-arm64-gnu": "4.52.2", "@rollup/rollup-linux-arm64-musl": "4.52.2", "@rollup/rollup-linux-loong64-gnu": "4.52.2", "@rollup/rollup-linux-ppc64-gnu": "4.52.2", "@rollup/rollup-linux-riscv64-gnu": "4.52.2", "@rollup/rollup-linux-riscv64-musl": "4.52.2", "@rollup/rollup-linux-s390x-gnu": "4.52.2", "@rollup/rollup-linux-x64-gnu": "4.52.2", "@rollup/rollup-linux-x64-musl": "4.52.2", "@rollup/rollup-openharmony-arm64": "4.52.2", "@rollup/rollup-win32-arm64-msvc": "4.52.2", "@rollup/rollup-win32-ia32-msvc": "4.52.2", "@rollup/rollup-win32-x64-gnu": "4.52.2", "@rollup/rollup-win32-x64-msvc": "4.52.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA=="], + + "runed": ["runed@0.31.1", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-v3czcTnO+EJjiPvD4dwIqfTdHLZ8oH0zJheKqAHh9QMViY7Qb29UlAMRpX7ZtHh7AFqV60KmfxaJ9QMy+L1igQ=="], + + "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], + + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], + + "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-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "style-to-object": ["style-to-object@1.0.9", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "svelte": ["svelte@5.39.5", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-YrTmQAgJNB5He5t14g+BH76xTjskcx0Dg3p6qKqfPcgha+9Rzdgkoazdk18ahzNfkFYgykZIjfnBvlPcp3NpYg=="], + + "svelte-check": ["svelte-check@4.3.2", "", { "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-71udP5w2kaSTcX8iV0hn3o2FWlabQHhJTJLIQrCqMsrcOeDUO2VhCQKKCA8AMVHSPwdxLEWkUWh9OKxns5PD9w=="], + + "svelte-sonner": ["svelte-sonner@1.0.5", "", { "dependencies": { "runed": "^0.28.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-9dpGPFqKb/QWudYqGnEz93vuY+NgCEvyNvxoCLMVGw6sDN/3oVeKV1xiEirW2E1N3vJEyj5imSBNOGltQHA7mg=="], + + "svelte-toolbelt": ["svelte-toolbelt@0.10.5", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.29.0", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.30.2" } }, "sha512-8e+eWTgxw1aiLxhDE8Rb1X6AoLitqpJz+WhAul2W7W58C8KoLoJQf1TgQdFPBiCPJ0Jg5y0Zi1uyua9em4VS0w=="], + + "tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="], + + "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], + + "tailwind-variants": ["tailwind-variants@3.1.1", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge"] }, "sha512-ftLXe3krnqkMHsuBTEmaVUXYovXtPyTK7ckEfDRXS8PBZx0bAUas+A0jYxuKA5b8qg++wvQ3d2MQ7l/xeZxbZQ=="], + + "tailwindcss": ["tailwindcss@4.1.13", "", {}, "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w=="], + + "tapable": ["tapable@2.2.3", "", {}, "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg=="], + + "tar": ["tar@7.5.1", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], + + "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=="], + + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + + "vite": ["vite@7.1.7", "", { "dependencies": { "esbuild": "^0.25.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-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="], + + "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], + + "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + + "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], + + "@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.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" }, "bundled": true }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "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=="], + + "svelte-sonner/runed": ["runed@0.28.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ=="], + + "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=="], + + "mode-watcher/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], + } +} diff --git a/web/components.json b/web/components.json new file mode 100644 index 0000000..c5d91b4 --- /dev/null +++ b/web/components.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://shadcn-svelte.com/schema.json", + "tailwind": { + "css": "src/app.css", + "baseColor": "slate" + }, + "aliases": { + "components": "$lib/components", + "utils": "$lib/utils", + "ui": "$lib/components/ui", + "hooks": "$lib/hooks", + "lib": "$lib" + }, + "typescript": true, + "registry": "https://shadcn-svelte.com/registry" +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..715ab6e --- /dev/null +++ b/web/package.json @@ -0,0 +1,35 @@ +{ + "name": "web", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "devDependencies": { + "@internationalized/date": "^3.8.1", + "@lucide/svelte": "^0.544.0", + "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/adapter-node": "^5.3.2", + "@sveltejs/kit": "^2.22.0", + "@sveltejs/vite-plugin-svelte": "^6.0.0", + "@tailwindcss/vite": "^4.0.0", + "bits-ui": "^2.11.0", + "clsx": "^2.1.1", + "mode-watcher": "^1.1.0", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "svelte-sonner": "^1.0.5", + "tailwind-merge": "^3.3.1", + "tailwind-variants": "^3.1.1", + "tailwindcss": "^4.0.0", + "tw-animate-css": "^1.4.0", + "typescript": "^5.0.0", + "vite": "^7.0.4" + } +} diff --git a/web/src/app.css b/web/src/app.css new file mode 100644 index 0000000..1f87c26 --- /dev/null +++ b/web/src/app.css @@ -0,0 +1,121 @@ +@import "tailwindcss"; + +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.129 0.042 264.695); + --card: oklch(1 0 0); + --card-foreground: oklch(0.129 0.042 264.695); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.129 0.042 264.695); + --primary: oklch(0.208 0.042 265.755); + --primary-foreground: oklch(0.984 0.003 247.858); + --secondary: oklch(0.968 0.007 247.896); + --secondary-foreground: oklch(0.208 0.042 265.755); + --muted: oklch(0.968 0.007 247.896); + --muted-foreground: oklch(0.554 0.046 257.417); + --accent: oklch(0.968 0.007 247.896); + --accent-foreground: oklch(0.208 0.042 265.755); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.929 0.013 255.508); + --input: oklch(0.929 0.013 255.508); + --ring: oklch(0.704 0.04 256.788); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.984 0.003 247.858); + --sidebar-foreground: oklch(0.129 0.042 264.695); + --sidebar-primary: oklch(0.208 0.042 265.755); + --sidebar-primary-foreground: oklch(0.984 0.003 247.858); + --sidebar-accent: oklch(0.968 0.007 247.896); + --sidebar-accent-foreground: oklch(0.208 0.042 265.755); + --sidebar-border: oklch(0.929 0.013 255.508); + --sidebar-ring: oklch(0.704 0.04 256.788); +} + +.dark { + --background: oklch(0.129 0.042 264.695); + --foreground: oklch(0.984 0.003 247.858); + --card: oklch(0.208 0.042 265.755); + --card-foreground: oklch(0.984 0.003 247.858); + --popover: oklch(0.208 0.042 265.755); + --popover-foreground: oklch(0.984 0.003 247.858); + --primary: oklch(0.929 0.013 255.508); + --primary-foreground: oklch(0.208 0.042 265.755); + --secondary: oklch(0.279 0.041 260.031); + --secondary-foreground: oklch(0.984 0.003 247.858); + --muted: oklch(0.279 0.041 260.031); + --muted-foreground: oklch(0.704 0.04 256.788); + --accent: oklch(0.279 0.041 260.031); + --accent-foreground: oklch(0.984 0.003 247.858); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.551 0.027 264.364); + --chart-1: oklch(53.943% 0.20646 265.678); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.208 0.042 265.755); + --sidebar-foreground: oklch(0.984 0.003 247.858); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.984 0.003 247.858); + --sidebar-accent: oklch(0.279 0.041 260.031); + --sidebar-accent-foreground: oklch(0.984 0.003 247.858); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.551 0.027 264.364); +} + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/web/src/app.d.ts b/web/src/app.d.ts new file mode 100644 index 0000000..da08e6d --- /dev/null +++ b/web/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/web/src/app.html b/web/src/app.html new file mode 100644 index 0000000..b666c1a --- /dev/null +++ b/web/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/web/src/lib/AppSidebar.svelte b/web/src/lib/AppSidebar.svelte new file mode 100644 index 0000000..b88e3b2 --- /dev/null +++ b/web/src/lib/AppSidebar.svelte @@ -0,0 +1,171 @@ + + + + +

⏭️ NextUp

+ +
+ + + {#each data.navMain as group (group.title)} + + {group.title} + + + {#each group.items as item (item.title)} + + + {#snippet child({ props })} + {item.title} + {/snippet} + + + {/each} + + + + {/each} + + +
\ No newline at end of file diff --git a/web/src/lib/ConnectionLostDialog.svelte b/web/src/lib/ConnectionLostDialog.svelte new file mode 100644 index 0000000..7f48956 --- /dev/null +++ b/web/src/lib/ConnectionLostDialog.svelte @@ -0,0 +1,24 @@ + + + + + + Verbindung verloren + + Die Verbindung zum Server wurde unterbrochen.
Die Verbindung wird automatisch wiederhergestellt. +
+
+
+
\ No newline at end of file diff --git a/web/src/lib/Spinner.svelte b/web/src/lib/Spinner.svelte new file mode 100644 index 0000000..e102f46 --- /dev/null +++ b/web/src/lib/Spinner.svelte @@ -0,0 +1,110 @@ +
+ + + + +
+ + \ No newline at end of file diff --git a/web/src/lib/assets/favicon.svg b/web/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/web/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte new file mode 100644 index 0000000..a005691 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte @@ -0,0 +1,18 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte new file mode 100644 index 0000000..a7b0cf7 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte @@ -0,0 +1,18 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte new file mode 100644 index 0000000..6c3c604 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte @@ -0,0 +1,27 @@ + + + + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte new file mode 100644 index 0000000..2ec67dc --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte new file mode 100644 index 0000000..f78b97a --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte new file mode 100644 index 0000000..c8fa762 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte new file mode 100644 index 0000000..a64ee76 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte @@ -0,0 +1,20 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte new file mode 100644 index 0000000..7ef2b5f --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte new file mode 100644 index 0000000..b22d1d5 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/index.ts b/web/src/lib/components/ui/alert-dialog/index.ts new file mode 100644 index 0000000..cc281c5 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/index.ts @@ -0,0 +1,39 @@ +import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; +import Trigger from "./alert-dialog-trigger.svelte"; +import Title from "./alert-dialog-title.svelte"; +import Action from "./alert-dialog-action.svelte"; +import Cancel from "./alert-dialog-cancel.svelte"; +import Footer from "./alert-dialog-footer.svelte"; +import Header from "./alert-dialog-header.svelte"; +import Overlay from "./alert-dialog-overlay.svelte"; +import Content from "./alert-dialog-content.svelte"; +import Description from "./alert-dialog-description.svelte"; + +const Root = AlertDialogPrimitive.Root; +const Portal = AlertDialogPrimitive.Portal; + +export { + Root, + Title, + Action, + Cancel, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + // + Root as AlertDialog, + Title as AlertDialogTitle, + Action as AlertDialogAction, + Cancel as AlertDialogCancel, + Portal as AlertDialogPortal, + Footer as AlertDialogFooter, + Header as AlertDialogHeader, + Trigger as AlertDialogTrigger, + Overlay as AlertDialogOverlay, + Content as AlertDialogContent, + Description as AlertDialogDescription, +}; diff --git a/web/src/lib/components/ui/badge/badge.svelte b/web/src/lib/components/ui/badge/badge.svelte new file mode 100644 index 0000000..5000457 --- /dev/null +++ b/web/src/lib/components/ui/badge/badge.svelte @@ -0,0 +1,50 @@ + + + + + + {@render children?.()} + diff --git a/web/src/lib/components/ui/badge/index.ts b/web/src/lib/components/ui/badge/index.ts new file mode 100644 index 0000000..64e0aa9 --- /dev/null +++ b/web/src/lib/components/ui/badge/index.ts @@ -0,0 +1,2 @@ +export { default as Badge } from "./badge.svelte"; +export { badgeVariants, type BadgeVariant } from "./badge.svelte"; diff --git a/web/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte b/web/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte new file mode 100644 index 0000000..a178cf5 --- /dev/null +++ b/web/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte @@ -0,0 +1,23 @@ + + + diff --git a/web/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte b/web/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte new file mode 100644 index 0000000..1a84c4c --- /dev/null +++ b/web/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte @@ -0,0 +1,20 @@ + + +
  • + {@render children?.()} +
  • diff --git a/web/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte b/web/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte new file mode 100644 index 0000000..e6bc17d --- /dev/null +++ b/web/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte @@ -0,0 +1,31 @@ + + +{#if child} + {@render child({ props: attrs })} +{:else} + + {@render children?.()} + +{/if} diff --git a/web/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte b/web/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte new file mode 100644 index 0000000..b5458fa --- /dev/null +++ b/web/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte @@ -0,0 +1,23 @@ + + +
      + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte b/web/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte new file mode 100644 index 0000000..5fb6979 --- /dev/null +++ b/web/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/web/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte b/web/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte new file mode 100644 index 0000000..84106a1 --- /dev/null +++ b/web/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte @@ -0,0 +1,27 @@ + + + diff --git a/web/src/lib/components/ui/breadcrumb/breadcrumb.svelte b/web/src/lib/components/ui/breadcrumb/breadcrumb.svelte new file mode 100644 index 0000000..8f8a3e6 --- /dev/null +++ b/web/src/lib/components/ui/breadcrumb/breadcrumb.svelte @@ -0,0 +1,21 @@ + + + diff --git a/web/src/lib/components/ui/breadcrumb/index.ts b/web/src/lib/components/ui/breadcrumb/index.ts new file mode 100644 index 0000000..dc914ec --- /dev/null +++ b/web/src/lib/components/ui/breadcrumb/index.ts @@ -0,0 +1,25 @@ +import Root from "./breadcrumb.svelte"; +import Ellipsis from "./breadcrumb-ellipsis.svelte"; +import Item from "./breadcrumb-item.svelte"; +import Separator from "./breadcrumb-separator.svelte"; +import Link from "./breadcrumb-link.svelte"; +import List from "./breadcrumb-list.svelte"; +import Page from "./breadcrumb-page.svelte"; + +export { + Root, + Ellipsis, + Item, + Separator, + Link, + List, + Page, + // + Root as Breadcrumb, + Ellipsis as BreadcrumbEllipsis, + Item as BreadcrumbItem, + Separator as BreadcrumbSeparator, + Link as BreadcrumbLink, + List as BreadcrumbList, + Page as BreadcrumbPage, +}; diff --git a/web/src/lib/components/ui/button/button.svelte b/web/src/lib/components/ui/button/button.svelte new file mode 100644 index 0000000..4daf453 --- /dev/null +++ b/web/src/lib/components/ui/button/button.svelte @@ -0,0 +1,80 @@ + + + + +{#if href} + + {@render children?.()} + +{:else} + +{/if} diff --git a/web/src/lib/components/ui/button/index.ts b/web/src/lib/components/ui/button/index.ts new file mode 100644 index 0000000..fb585d7 --- /dev/null +++ b/web/src/lib/components/ui/button/index.ts @@ -0,0 +1,17 @@ +import Root, { + type ButtonProps, + type ButtonSize, + type ButtonVariant, + buttonVariants, +} from "./button.svelte"; + +export { + Root, + type ButtonProps as Props, + // + Root as Button, + buttonVariants, + type ButtonProps, + type ButtonSize, + type ButtonVariant, +}; diff --git a/web/src/lib/components/ui/card/card-action.svelte b/web/src/lib/components/ui/card/card-action.svelte new file mode 100644 index 0000000..cc36c56 --- /dev/null +++ b/web/src/lib/components/ui/card/card-action.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/card/card-content.svelte b/web/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000..bc90b83 --- /dev/null +++ b/web/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,15 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/card/card-description.svelte b/web/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000..9b20ac7 --- /dev/null +++ b/web/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,20 @@ + + +

    + {@render children?.()} +

    diff --git a/web/src/lib/components/ui/card/card-footer.svelte b/web/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000..cf43353 --- /dev/null +++ b/web/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/card/card-header.svelte b/web/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000..8a91abb --- /dev/null +++ b/web/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/card/card-title.svelte b/web/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000..22586e6 --- /dev/null +++ b/web/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/card/card.svelte b/web/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000..99448cc --- /dev/null +++ b/web/src/lib/components/ui/card/card.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/card/index.ts b/web/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000..4d3fce4 --- /dev/null +++ b/web/src/lib/components/ui/card/index.ts @@ -0,0 +1,25 @@ +import Root from "./card.svelte"; +import Content from "./card-content.svelte"; +import Description from "./card-description.svelte"; +import Footer from "./card-footer.svelte"; +import Header from "./card-header.svelte"; +import Title from "./card-title.svelte"; +import Action from "./card-action.svelte"; + +export { + Root, + Content, + Description, + Footer, + Header, + Title, + Action, + // + Root as Card, + Content as CardContent, + Description as CardDescription, + Footer as CardFooter, + Header as CardHeader, + Title as CardTitle, + Action as CardAction, +}; diff --git a/web/src/lib/components/ui/dialog/dialog-close.svelte b/web/src/lib/components/ui/dialog/dialog-close.svelte new file mode 100644 index 0000000..840b2f6 --- /dev/null +++ b/web/src/lib/components/ui/dialog/dialog-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/dialog/dialog-content.svelte b/web/src/lib/components/ui/dialog/dialog-content.svelte new file mode 100644 index 0000000..a647d56 --- /dev/null +++ b/web/src/lib/components/ui/dialog/dialog-content.svelte @@ -0,0 +1,43 @@ + + + + + + {@render children?.()} + {#if showCloseButton} + + + Close + + {/if} + + diff --git a/web/src/lib/components/ui/dialog/dialog-description.svelte b/web/src/lib/components/ui/dialog/dialog-description.svelte new file mode 100644 index 0000000..3845023 --- /dev/null +++ b/web/src/lib/components/ui/dialog/dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/dialog/dialog-footer.svelte b/web/src/lib/components/ui/dialog/dialog-footer.svelte new file mode 100644 index 0000000..e7ff446 --- /dev/null +++ b/web/src/lib/components/ui/dialog/dialog-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/dialog/dialog-header.svelte b/web/src/lib/components/ui/dialog/dialog-header.svelte new file mode 100644 index 0000000..fc90cd9 --- /dev/null +++ b/web/src/lib/components/ui/dialog/dialog-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/dialog/dialog-overlay.svelte b/web/src/lib/components/ui/dialog/dialog-overlay.svelte new file mode 100644 index 0000000..f81ad83 --- /dev/null +++ b/web/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -0,0 +1,20 @@ + + + diff --git a/web/src/lib/components/ui/dialog/dialog-title.svelte b/web/src/lib/components/ui/dialog/dialog-title.svelte new file mode 100644 index 0000000..067e55e --- /dev/null +++ b/web/src/lib/components/ui/dialog/dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/dialog/dialog-trigger.svelte b/web/src/lib/components/ui/dialog/dialog-trigger.svelte new file mode 100644 index 0000000..9d1e801 --- /dev/null +++ b/web/src/lib/components/ui/dialog/dialog-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/dialog/index.ts b/web/src/lib/components/ui/dialog/index.ts new file mode 100644 index 0000000..dce1d9d --- /dev/null +++ b/web/src/lib/components/ui/dialog/index.ts @@ -0,0 +1,37 @@ +import { Dialog as DialogPrimitive } from "bits-ui"; + +import Title from "./dialog-title.svelte"; +import Footer from "./dialog-footer.svelte"; +import Header from "./dialog-header.svelte"; +import Overlay from "./dialog-overlay.svelte"; +import Content from "./dialog-content.svelte"; +import Description from "./dialog-description.svelte"; +import Trigger from "./dialog-trigger.svelte"; +import Close from "./dialog-close.svelte"; + +const Root = DialogPrimitive.Root; +const Portal = DialogPrimitive.Portal; + +export { + Root, + Title, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Close, + // + Root as Dialog, + Title as DialogTitle, + Portal as DialogPortal, + Footer as DialogFooter, + Header as DialogHeader, + Trigger as DialogTrigger, + Overlay as DialogOverlay, + Content as DialogContent, + Description as DialogDescription, + Close as DialogClose, +}; diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte new file mode 100644 index 0000000..e03f949 --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte @@ -0,0 +1,41 @@ + + + + {#snippet children({ checked, indeterminate })} + + {#if indeterminate} + + {:else} + + {/if} + + {@render childrenProp?.()} + {/snippet} + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte new file mode 100644 index 0000000..907ef73 --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte @@ -0,0 +1,27 @@ + + + + + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte new file mode 100644 index 0000000..48d14a9 --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte @@ -0,0 +1,22 @@ + + + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte new file mode 100644 index 0000000..aca1f7b --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte new file mode 100644 index 0000000..64bb283 --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte @@ -0,0 +1,27 @@ + + + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte new file mode 100644 index 0000000..f72e477 --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte new file mode 100644 index 0000000..189aef4 --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte new file mode 100644 index 0000000..513170a --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte @@ -0,0 +1,31 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.({ checked })} + {/snippet} + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte new file mode 100644 index 0000000..90f1b6f --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte new file mode 100644 index 0000000..6974947 --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte new file mode 100644 index 0000000..10e14ca --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte @@ -0,0 +1,20 @@ + + + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte new file mode 100644 index 0000000..f9b286a --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte new file mode 100644 index 0000000..cb05344 --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/dropdown-menu/index.ts b/web/src/lib/components/ui/dropdown-menu/index.ts new file mode 100644 index 0000000..1cf9f70 --- /dev/null +++ b/web/src/lib/components/ui/dropdown-menu/index.ts @@ -0,0 +1,49 @@ +import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; +import CheckboxItem from "./dropdown-menu-checkbox-item.svelte"; +import Content from "./dropdown-menu-content.svelte"; +import Group from "./dropdown-menu-group.svelte"; +import Item from "./dropdown-menu-item.svelte"; +import Label from "./dropdown-menu-label.svelte"; +import RadioGroup from "./dropdown-menu-radio-group.svelte"; +import RadioItem from "./dropdown-menu-radio-item.svelte"; +import Separator from "./dropdown-menu-separator.svelte"; +import Shortcut from "./dropdown-menu-shortcut.svelte"; +import Trigger from "./dropdown-menu-trigger.svelte"; +import SubContent from "./dropdown-menu-sub-content.svelte"; +import SubTrigger from "./dropdown-menu-sub-trigger.svelte"; +import GroupHeading from "./dropdown-menu-group-heading.svelte"; +const Sub = DropdownMenuPrimitive.Sub; +const Root = DropdownMenuPrimitive.Root; + +export { + CheckboxItem, + Content, + Root as DropdownMenu, + CheckboxItem as DropdownMenuCheckboxItem, + Content as DropdownMenuContent, + Group as DropdownMenuGroup, + Item as DropdownMenuItem, + Label as DropdownMenuLabel, + RadioGroup as DropdownMenuRadioGroup, + RadioItem as DropdownMenuRadioItem, + Separator as DropdownMenuSeparator, + Shortcut as DropdownMenuShortcut, + Sub as DropdownMenuSub, + SubContent as DropdownMenuSubContent, + SubTrigger as DropdownMenuSubTrigger, + Trigger as DropdownMenuTrigger, + GroupHeading as DropdownMenuGroupHeading, + Group, + GroupHeading, + Item, + Label, + RadioGroup, + RadioItem, + Root, + Separator, + Shortcut, + Sub, + SubContent, + SubTrigger, + Trigger, +}; diff --git a/web/src/lib/components/ui/input-otp/index.ts b/web/src/lib/components/ui/input-otp/index.ts new file mode 100644 index 0000000..e9ae273 --- /dev/null +++ b/web/src/lib/components/ui/input-otp/index.ts @@ -0,0 +1,15 @@ +import Root from "./input-otp.svelte"; +import Group from "./input-otp-group.svelte"; +import Slot from "./input-otp-slot.svelte"; +import Separator from "./input-otp-separator.svelte"; + +export { + Root, + Group, + Slot, + Separator, + Root as InputOTP, + Group as InputOTPGroup, + Slot as InputOTPSlot, + Separator as InputOTPSeparator, +}; diff --git a/web/src/lib/components/ui/input-otp/input-otp-group.svelte b/web/src/lib/components/ui/input-otp/input-otp-group.svelte new file mode 100644 index 0000000..9b311bc --- /dev/null +++ b/web/src/lib/components/ui/input-otp/input-otp-group.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/input-otp/input-otp-separator.svelte b/web/src/lib/components/ui/input-otp/input-otp-separator.svelte new file mode 100644 index 0000000..3241295 --- /dev/null +++ b/web/src/lib/components/ui/input-otp/input-otp-separator.svelte @@ -0,0 +1,19 @@ + + +
    + {#if children} + {@render children?.()} + {:else} + + {/if} +
    diff --git a/web/src/lib/components/ui/input-otp/input-otp-slot.svelte b/web/src/lib/components/ui/input-otp/input-otp-slot.svelte new file mode 100644 index 0000000..34d2636 --- /dev/null +++ b/web/src/lib/components/ui/input-otp/input-otp-slot.svelte @@ -0,0 +1,31 @@ + + + + {cell.char} + {#if cell.hasFakeCaret} +
    + +
    + {/if} +
    diff --git a/web/src/lib/components/ui/input-otp/input-otp.svelte b/web/src/lib/components/ui/input-otp/input-otp.svelte new file mode 100644 index 0000000..e27cbcc --- /dev/null +++ b/web/src/lib/components/ui/input-otp/input-otp.svelte @@ -0,0 +1,22 @@ + + + diff --git a/web/src/lib/components/ui/input/index.ts b/web/src/lib/components/ui/input/index.ts new file mode 100644 index 0000000..f47b6d3 --- /dev/null +++ b/web/src/lib/components/ui/input/index.ts @@ -0,0 +1,7 @@ +import Root from "./input.svelte"; + +export { + Root, + // + Root as Input, +}; diff --git a/web/src/lib/components/ui/input/input.svelte b/web/src/lib/components/ui/input/input.svelte new file mode 100644 index 0000000..19c6dae --- /dev/null +++ b/web/src/lib/components/ui/input/input.svelte @@ -0,0 +1,51 @@ + + +{#if type === "file"} + +{:else} + +{/if} diff --git a/web/src/lib/components/ui/label/index.ts b/web/src/lib/components/ui/label/index.ts new file mode 100644 index 0000000..8bfca0b --- /dev/null +++ b/web/src/lib/components/ui/label/index.ts @@ -0,0 +1,7 @@ +import Root from "./label.svelte"; + +export { + Root, + // + Root as Label, +}; diff --git a/web/src/lib/components/ui/label/label.svelte b/web/src/lib/components/ui/label/label.svelte new file mode 100644 index 0000000..d0afda3 --- /dev/null +++ b/web/src/lib/components/ui/label/label.svelte @@ -0,0 +1,20 @@ + + + diff --git a/web/src/lib/components/ui/separator/index.ts b/web/src/lib/components/ui/separator/index.ts new file mode 100644 index 0000000..82442d2 --- /dev/null +++ b/web/src/lib/components/ui/separator/index.ts @@ -0,0 +1,7 @@ +import Root from "./separator.svelte"; + +export { + Root, + // + Root as Separator, +}; diff --git a/web/src/lib/components/ui/separator/separator.svelte b/web/src/lib/components/ui/separator/separator.svelte new file mode 100644 index 0000000..09d88f4 --- /dev/null +++ b/web/src/lib/components/ui/separator/separator.svelte @@ -0,0 +1,20 @@ + + + diff --git a/web/src/lib/components/ui/sheet/index.ts b/web/src/lib/components/ui/sheet/index.ts new file mode 100644 index 0000000..01d40c8 --- /dev/null +++ b/web/src/lib/components/ui/sheet/index.ts @@ -0,0 +1,36 @@ +import { Dialog as SheetPrimitive } from "bits-ui"; +import Trigger from "./sheet-trigger.svelte"; +import Close from "./sheet-close.svelte"; +import Overlay from "./sheet-overlay.svelte"; +import Content from "./sheet-content.svelte"; +import Header from "./sheet-header.svelte"; +import Footer from "./sheet-footer.svelte"; +import Title from "./sheet-title.svelte"; +import Description from "./sheet-description.svelte"; + +const Root = SheetPrimitive.Root; +const Portal = SheetPrimitive.Portal; + +export { + Root, + Close, + Trigger, + Portal, + Overlay, + Content, + Header, + Footer, + Title, + Description, + // + Root as Sheet, + Close as SheetClose, + Trigger as SheetTrigger, + Portal as SheetPortal, + Overlay as SheetOverlay, + Content as SheetContent, + Header as SheetHeader, + Footer as SheetFooter, + Title as SheetTitle, + Description as SheetDescription, +}; diff --git a/web/src/lib/components/ui/sheet/sheet-close.svelte b/web/src/lib/components/ui/sheet/sheet-close.svelte new file mode 100644 index 0000000..ae382c1 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/sheet/sheet-content.svelte b/web/src/lib/components/ui/sheet/sheet-content.svelte new file mode 100644 index 0000000..856922e --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-content.svelte @@ -0,0 +1,58 @@ + + + + + + + + {@render children?.()} + + + Close + + + diff --git a/web/src/lib/components/ui/sheet/sheet-description.svelte b/web/src/lib/components/ui/sheet/sheet-description.svelte new file mode 100644 index 0000000..333b17a --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/sheet/sheet-footer.svelte b/web/src/lib/components/ui/sheet/sheet-footer.svelte new file mode 100644 index 0000000..dd9ed84 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sheet/sheet-header.svelte b/web/src/lib/components/ui/sheet/sheet-header.svelte new file mode 100644 index 0000000..757a6a5 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sheet/sheet-overlay.svelte b/web/src/lib/components/ui/sheet/sheet-overlay.svelte new file mode 100644 index 0000000..345e197 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-overlay.svelte @@ -0,0 +1,20 @@ + + + diff --git a/web/src/lib/components/ui/sheet/sheet-title.svelte b/web/src/lib/components/ui/sheet/sheet-title.svelte new file mode 100644 index 0000000..9fda327 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/sheet/sheet-trigger.svelte b/web/src/lib/components/ui/sheet/sheet-trigger.svelte new file mode 100644 index 0000000..e266975 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/sidebar/constants.ts b/web/src/lib/components/ui/sidebar/constants.ts new file mode 100644 index 0000000..4de4435 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/constants.ts @@ -0,0 +1,6 @@ +export const SIDEBAR_COOKIE_NAME = "sidebar:state"; +export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; +export const SIDEBAR_WIDTH = "16rem"; +export const SIDEBAR_WIDTH_MOBILE = "18rem"; +export const SIDEBAR_WIDTH_ICON = "3rem"; +export const SIDEBAR_KEYBOARD_SHORTCUT = "b"; diff --git a/web/src/lib/components/ui/sidebar/context.svelte.ts b/web/src/lib/components/ui/sidebar/context.svelte.ts new file mode 100644 index 0000000..15248ad --- /dev/null +++ b/web/src/lib/components/ui/sidebar/context.svelte.ts @@ -0,0 +1,81 @@ +import { IsMobile } from "$lib/hooks/is-mobile.svelte.js"; +import { getContext, setContext } from "svelte"; +import { SIDEBAR_KEYBOARD_SHORTCUT } from "./constants.js"; + +type Getter = () => T; + +export type SidebarStateProps = { + /** + * A getter function that returns the current open state of the sidebar. + * We use a getter function here to support `bind:open` on the `Sidebar.Provider` + * component. + */ + open: Getter; + + /** + * A function that sets the open state of the sidebar. To support `bind:open`, we need + * a source of truth for changing the open state to ensure it will be synced throughout + * the sub-components and any `bind:` references. + */ + setOpen: (open: boolean) => void; +}; + +class SidebarState { + readonly props: SidebarStateProps; + open = $derived.by(() => this.props.open()); + openMobile = $state(false); + setOpen: SidebarStateProps["setOpen"]; + #isMobile: IsMobile; + state = $derived.by(() => (this.open ? "expanded" : "collapsed")); + + constructor(props: SidebarStateProps) { + this.setOpen = props.setOpen; + this.#isMobile = new IsMobile(); + this.props = props; + } + + // Convenience getter for checking if the sidebar is mobile + // without this, we would need to use `sidebar.isMobile.current` everywhere + get isMobile() { + return this.#isMobile.current; + } + + // Event handler to apply to the `` + handleShortcutKeydown = (e: KeyboardEvent) => { + if (e.key === SIDEBAR_KEYBOARD_SHORTCUT && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + this.toggle(); + } + }; + + setOpenMobile = (value: boolean) => { + this.openMobile = value; + }; + + toggle = () => { + return this.#isMobile.current + ? (this.openMobile = !this.openMobile) + : this.setOpen(!this.open); + }; +} + +const SYMBOL_KEY = "scn-sidebar"; + +/** + * Instantiates a new `SidebarState` instance and sets it in the context. + * + * @param props The constructor props for the `SidebarState` class. + * @returns The `SidebarState` instance. + */ +export function setSidebar(props: SidebarStateProps): SidebarState { + return setContext(Symbol.for(SYMBOL_KEY), new SidebarState(props)); +} + +/** + * Retrieves the `SidebarState` instance from the context. This is a class instance, + * so you cannot destructure it. + * @returns The `SidebarState` instance. + */ +export function useSidebar(): SidebarState { + return getContext(Symbol.for(SYMBOL_KEY)); +} diff --git a/web/src/lib/components/ui/sidebar/index.ts b/web/src/lib/components/ui/sidebar/index.ts new file mode 100644 index 0000000..318a341 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/index.ts @@ -0,0 +1,75 @@ +import { useSidebar } from "./context.svelte.js"; +import Content from "./sidebar-content.svelte"; +import Footer from "./sidebar-footer.svelte"; +import GroupAction from "./sidebar-group-action.svelte"; +import GroupContent from "./sidebar-group-content.svelte"; +import GroupLabel from "./sidebar-group-label.svelte"; +import Group from "./sidebar-group.svelte"; +import Header from "./sidebar-header.svelte"; +import Input from "./sidebar-input.svelte"; +import Inset from "./sidebar-inset.svelte"; +import MenuAction from "./sidebar-menu-action.svelte"; +import MenuBadge from "./sidebar-menu-badge.svelte"; +import MenuButton from "./sidebar-menu-button.svelte"; +import MenuItem from "./sidebar-menu-item.svelte"; +import MenuSkeleton from "./sidebar-menu-skeleton.svelte"; +import MenuSubButton from "./sidebar-menu-sub-button.svelte"; +import MenuSubItem from "./sidebar-menu-sub-item.svelte"; +import MenuSub from "./sidebar-menu-sub.svelte"; +import Menu from "./sidebar-menu.svelte"; +import Provider from "./sidebar-provider.svelte"; +import Rail from "./sidebar-rail.svelte"; +import Separator from "./sidebar-separator.svelte"; +import Trigger from "./sidebar-trigger.svelte"; +import Root from "./sidebar.svelte"; + +export { + Content, + Footer, + Group, + GroupAction, + GroupContent, + GroupLabel, + Header, + Input, + Inset, + Menu, + MenuAction, + MenuBadge, + MenuButton, + MenuItem, + MenuSkeleton, + MenuSub, + MenuSubButton, + MenuSubItem, + Provider, + Rail, + Root, + Separator, + // + Root as Sidebar, + Content as SidebarContent, + Footer as SidebarFooter, + Group as SidebarGroup, + GroupAction as SidebarGroupAction, + GroupContent as SidebarGroupContent, + GroupLabel as SidebarGroupLabel, + Header as SidebarHeader, + Input as SidebarInput, + Inset as SidebarInset, + Menu as SidebarMenu, + MenuAction as SidebarMenuAction, + MenuBadge as SidebarMenuBadge, + MenuButton as SidebarMenuButton, + MenuItem as SidebarMenuItem, + MenuSkeleton as SidebarMenuSkeleton, + MenuSub as SidebarMenuSub, + MenuSubButton as SidebarMenuSubButton, + MenuSubItem as SidebarMenuSubItem, + Provider as SidebarProvider, + Rail as SidebarRail, + Separator as SidebarSeparator, + Trigger as SidebarTrigger, + Trigger, + useSidebar, +}; diff --git a/web/src/lib/components/ui/sidebar/sidebar-content.svelte b/web/src/lib/components/ui/sidebar/sidebar-content.svelte new file mode 100644 index 0000000..f121800 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-content.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-footer.svelte b/web/src/lib/components/ui/sidebar/sidebar-footer.svelte new file mode 100644 index 0000000..6259cb9 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-footer.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-group-action.svelte b/web/src/lib/components/ui/sidebar/sidebar-group-action.svelte new file mode 100644 index 0000000..fb84e4a --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-group-action.svelte @@ -0,0 +1,36 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} + +{/if} diff --git a/web/src/lib/components/ui/sidebar/sidebar-group-content.svelte b/web/src/lib/components/ui/sidebar/sidebar-group-content.svelte new file mode 100644 index 0000000..415255f --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-group-content.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-group-label.svelte b/web/src/lib/components/ui/sidebar/sidebar-group-label.svelte new file mode 100644 index 0000000..e292945 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-group-label.svelte @@ -0,0 +1,34 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} +
    + {@render children?.()} +
    +{/if} diff --git a/web/src/lib/components/ui/sidebar/sidebar-group.svelte b/web/src/lib/components/ui/sidebar/sidebar-group.svelte new file mode 100644 index 0000000..ec18a69 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-group.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-header.svelte b/web/src/lib/components/ui/sidebar/sidebar-header.svelte new file mode 100644 index 0000000..a1b2db1 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-header.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-input.svelte b/web/src/lib/components/ui/sidebar/sidebar-input.svelte new file mode 100644 index 0000000..19b3666 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-input.svelte @@ -0,0 +1,21 @@ + + + diff --git a/web/src/lib/components/ui/sidebar/sidebar-inset.svelte b/web/src/lib/components/ui/sidebar/sidebar-inset.svelte new file mode 100644 index 0000000..d862761 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-inset.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-menu-action.svelte b/web/src/lib/components/ui/sidebar/sidebar-menu-action.svelte new file mode 100644 index 0000000..fa3fb0c --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-menu-action.svelte @@ -0,0 +1,43 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} + +{/if} diff --git a/web/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte b/web/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte new file mode 100644 index 0000000..69e5a3c --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte @@ -0,0 +1,29 @@ + + +
    + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-menu-button.svelte b/web/src/lib/components/ui/sidebar/sidebar-menu-button.svelte new file mode 100644 index 0000000..4bef683 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-menu-button.svelte @@ -0,0 +1,103 @@ + + + + +{#snippet Button({ props }: { props?: Record })} + {@const mergedProps = mergeProps(buttonProps, props)} + {#if child} + {@render child({ props: mergedProps })} + {:else} + + {/if} +{/snippet} + +{#if !tooltipContent} + {@render Button({})} +{:else} + + + {#snippet child({ props })} + {@render Button({ props })} + {/snippet} + + + +{/if} diff --git a/web/src/lib/components/ui/sidebar/sidebar-menu-item.svelte b/web/src/lib/components/ui/sidebar/sidebar-menu-item.svelte new file mode 100644 index 0000000..4db4453 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-menu-item.svelte @@ -0,0 +1,21 @@ + + +
  • + {@render children?.()} +
  • diff --git a/web/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte b/web/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte new file mode 100644 index 0000000..cc63b04 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte @@ -0,0 +1,36 @@ + + +
    + {#if showIcon} + + {/if} + + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte b/web/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte new file mode 100644 index 0000000..987f104 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte @@ -0,0 +1,43 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} + + {@render children?.()} + +{/if} diff --git a/web/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte b/web/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte new file mode 100644 index 0000000..681d0f1 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte @@ -0,0 +1,21 @@ + + +
  • + {@render children?.()} +
  • diff --git a/web/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte b/web/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte new file mode 100644 index 0000000..8ab1111 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte @@ -0,0 +1,25 @@ + + +
      + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-menu.svelte b/web/src/lib/components/ui/sidebar/sidebar-menu.svelte new file mode 100644 index 0000000..946ccce --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-menu.svelte @@ -0,0 +1,21 @@ + + +
      + {@render children?.()} +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-provider.svelte b/web/src/lib/components/ui/sidebar/sidebar-provider.svelte new file mode 100644 index 0000000..5b0d0aa --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-provider.svelte @@ -0,0 +1,53 @@ + + + + + +
    + {@render children?.()} +
    +
    diff --git a/web/src/lib/components/ui/sidebar/sidebar-rail.svelte b/web/src/lib/components/ui/sidebar/sidebar-rail.svelte new file mode 100644 index 0000000..c180cf5 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-rail.svelte @@ -0,0 +1,36 @@ + + + diff --git a/web/src/lib/components/ui/sidebar/sidebar-separator.svelte b/web/src/lib/components/ui/sidebar/sidebar-separator.svelte new file mode 100644 index 0000000..5a7deda --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-separator.svelte @@ -0,0 +1,19 @@ + + + diff --git a/web/src/lib/components/ui/sidebar/sidebar-trigger.svelte b/web/src/lib/components/ui/sidebar/sidebar-trigger.svelte new file mode 100644 index 0000000..1825182 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar-trigger.svelte @@ -0,0 +1,35 @@ + + + diff --git a/web/src/lib/components/ui/sidebar/sidebar.svelte b/web/src/lib/components/ui/sidebar/sidebar.svelte new file mode 100644 index 0000000..3e9eba9 --- /dev/null +++ b/web/src/lib/components/ui/sidebar/sidebar.svelte @@ -0,0 +1,104 @@ + + +{#if collapsible === "none"} +
    + {@render children?.()} +
    +{:else if sidebar.isMobile} + sidebar.openMobile, (v) => sidebar.setOpenMobile(v)} + {...restProps} + > + + + Sidebar + Displays the mobile sidebar. + +
    + {@render children?.()} +
    +
    +
    +{:else} + +{/if} diff --git a/web/src/lib/components/ui/skeleton/index.ts b/web/src/lib/components/ui/skeleton/index.ts new file mode 100644 index 0000000..186db21 --- /dev/null +++ b/web/src/lib/components/ui/skeleton/index.ts @@ -0,0 +1,7 @@ +import Root from "./skeleton.svelte"; + +export { + Root, + // + Root as Skeleton, +}; diff --git a/web/src/lib/components/ui/skeleton/skeleton.svelte b/web/src/lib/components/ui/skeleton/skeleton.svelte new file mode 100644 index 0000000..c7e3d26 --- /dev/null +++ b/web/src/lib/components/ui/skeleton/skeleton.svelte @@ -0,0 +1,17 @@ + + +
    diff --git a/web/src/lib/components/ui/sonner/index.ts b/web/src/lib/components/ui/sonner/index.ts new file mode 100644 index 0000000..1ad9f4a --- /dev/null +++ b/web/src/lib/components/ui/sonner/index.ts @@ -0,0 +1 @@ +export { default as Toaster } from "./sonner.svelte"; diff --git a/web/src/lib/components/ui/sonner/sonner.svelte b/web/src/lib/components/ui/sonner/sonner.svelte new file mode 100644 index 0000000..1f50e1e --- /dev/null +++ b/web/src/lib/components/ui/sonner/sonner.svelte @@ -0,0 +1,13 @@ + + + diff --git a/web/src/lib/components/ui/table/index.ts b/web/src/lib/components/ui/table/index.ts new file mode 100644 index 0000000..14695c8 --- /dev/null +++ b/web/src/lib/components/ui/table/index.ts @@ -0,0 +1,28 @@ +import Root from "./table.svelte"; +import Body from "./table-body.svelte"; +import Caption from "./table-caption.svelte"; +import Cell from "./table-cell.svelte"; +import Footer from "./table-footer.svelte"; +import Head from "./table-head.svelte"; +import Header from "./table-header.svelte"; +import Row from "./table-row.svelte"; + +export { + Root, + Body, + Caption, + Cell, + Footer, + Head, + Header, + Row, + // + Root as Table, + Body as TableBody, + Caption as TableCaption, + Cell as TableCell, + Footer as TableFooter, + Head as TableHead, + Header as TableHeader, + Row as TableRow, +}; diff --git a/web/src/lib/components/ui/table/table-body.svelte b/web/src/lib/components/ui/table/table-body.svelte new file mode 100644 index 0000000..29e9687 --- /dev/null +++ b/web/src/lib/components/ui/table/table-body.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/web/src/lib/components/ui/table/table-caption.svelte b/web/src/lib/components/ui/table/table-caption.svelte new file mode 100644 index 0000000..4696cff --- /dev/null +++ b/web/src/lib/components/ui/table/table-caption.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/web/src/lib/components/ui/table/table-cell.svelte b/web/src/lib/components/ui/table/table-cell.svelte new file mode 100644 index 0000000..1a2f033 --- /dev/null +++ b/web/src/lib/components/ui/table/table-cell.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/web/src/lib/components/ui/table/table-footer.svelte b/web/src/lib/components/ui/table/table-footer.svelte new file mode 100644 index 0000000..b9b14eb --- /dev/null +++ b/web/src/lib/components/ui/table/table-footer.svelte @@ -0,0 +1,20 @@ + + +tr]:last:border-b-0", className)} + {...restProps} +> + {@render children?.()} + diff --git a/web/src/lib/components/ui/table/table-head.svelte b/web/src/lib/components/ui/table/table-head.svelte new file mode 100644 index 0000000..e9dd237 --- /dev/null +++ b/web/src/lib/components/ui/table/table-head.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/web/src/lib/components/ui/table/table-header.svelte b/web/src/lib/components/ui/table/table-header.svelte new file mode 100644 index 0000000..f47d259 --- /dev/null +++ b/web/src/lib/components/ui/table/table-header.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/web/src/lib/components/ui/table/table-row.svelte b/web/src/lib/components/ui/table/table-row.svelte new file mode 100644 index 0000000..0df769e --- /dev/null +++ b/web/src/lib/components/ui/table/table-row.svelte @@ -0,0 +1,23 @@ + + +svelte-css-wrapper]:[&>th,td]:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors", + className + )} + {...restProps} +> + {@render children?.()} + diff --git a/web/src/lib/components/ui/table/table.svelte b/web/src/lib/components/ui/table/table.svelte new file mode 100644 index 0000000..a334956 --- /dev/null +++ b/web/src/lib/components/ui/table/table.svelte @@ -0,0 +1,22 @@ + + +
    + + {@render children?.()} +
    +
    diff --git a/web/src/lib/components/ui/tooltip/index.ts b/web/src/lib/components/ui/tooltip/index.ts new file mode 100644 index 0000000..313a7f0 --- /dev/null +++ b/web/src/lib/components/ui/tooltip/index.ts @@ -0,0 +1,21 @@ +import { Tooltip as TooltipPrimitive } from "bits-ui"; +import Trigger from "./tooltip-trigger.svelte"; +import Content from "./tooltip-content.svelte"; + +const Root = TooltipPrimitive.Root; +const Provider = TooltipPrimitive.Provider; +const Portal = TooltipPrimitive.Portal; + +export { + Root, + Trigger, + Content, + Provider, + Portal, + // + Root as Tooltip, + Content as TooltipContent, + Trigger as TooltipTrigger, + Provider as TooltipProvider, + Portal as TooltipPortal, +}; diff --git a/web/src/lib/components/ui/tooltip/tooltip-content.svelte b/web/src/lib/components/ui/tooltip/tooltip-content.svelte new file mode 100644 index 0000000..e495efe --- /dev/null +++ b/web/src/lib/components/ui/tooltip/tooltip-content.svelte @@ -0,0 +1,47 @@ + + + + + {@render children?.()} + + {#snippet child({ props })} +
    + {/snippet} +
    +
    +
    diff --git a/web/src/lib/components/ui/tooltip/tooltip-trigger.svelte b/web/src/lib/components/ui/tooltip/tooltip-trigger.svelte new file mode 100644 index 0000000..1acdaa4 --- /dev/null +++ b/web/src/lib/components/ui/tooltip/tooltip-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/hooks/is-mobile.svelte.ts b/web/src/lib/hooks/is-mobile.svelte.ts new file mode 100644 index 0000000..4829c00 --- /dev/null +++ b/web/src/lib/hooks/is-mobile.svelte.ts @@ -0,0 +1,9 @@ +import { MediaQuery } from "svelte/reactivity"; + +const DEFAULT_MOBILE_BREAKPOINT = 768; + +export class IsMobile extends MediaQuery { + constructor(breakpoint: number = DEFAULT_MOBILE_BREAKPOINT) { + super(`max-width: ${breakpoint - 1}px`); + } +} diff --git a/web/src/lib/index.ts b/web/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/web/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/web/src/lib/search-form.svelte b/web/src/lib/search-form.svelte new file mode 100644 index 0000000..463a9a1 --- /dev/null +++ b/web/src/lib/search-form.svelte @@ -0,0 +1,21 @@ + + +
    + + + + + + + +
    diff --git a/web/src/lib/switcher.svelte b/web/src/lib/switcher.svelte new file mode 100644 index 0000000..039596c --- /dev/null +++ b/web/src/lib/switcher.svelte @@ -0,0 +1,45 @@ + + + + + + {#snippet child({ props })} + +
    + +
    +
    + Documentation + v{selectedVersion} +
    + +
    + {/snippet} +
    + + {#each versions as version (version)} + (selectedVersion = version)}> + v{version} + {#if version === selectedVersion} + + {/if} + + {/each} + +
    +
    +
    \ No newline at end of file diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts new file mode 100644 index 0000000..55b3a91 --- /dev/null +++ b/web/src/lib/utils.ts @@ -0,0 +1,13 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChild = T extends { child?: any } ? Omit : T; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChildren = T extends { children?: any } ? Omit : T; +export type WithoutChildrenOrChild = WithoutChildren>; +export type WithElementRef = T & { ref?: U | null }; diff --git a/web/src/lib/ws.svelte.ts b/web/src/lib/ws.svelte.ts new file mode 100644 index 0000000..a8e2681 --- /dev/null +++ b/web/src/lib/ws.svelte.ts @@ -0,0 +1,57 @@ +import { toast } from "svelte-sonner"; + +let ws: WebSocket; + +export const wsState = $state({ + connected: false, + closed: false +}) + +export const eventTarget = new EventTarget(); + +let sendQueue: any[] = []; + +eventTarget.addEventListener("send", (e) => { + const ev = e as CustomEvent; + if(!ws || ws.readyState !== WebSocket.OPEN) { + sendQueue.push(ev.detail); + return; + } + ws.send(JSON.stringify(ev.detail)); +}); + +export function connectWS() { + ws = new WebSocket(`ws://localhost:3000`); + console.log("Connecting to WebSocket..."); + + ws.addEventListener("open", () => { + for(const msg of sendQueue) { + ws.send(JSON.stringify(msg)); + } + }); + + ws.addEventListener("close", () => { + wsState.connected = false; + toast.error("Verbindung getrennt"); + console.log("WebSocket closed"); + wsState.closed = true; + }); + + ws.addEventListener("error", () => { + ws.close(); + wsState.connected = false; + toast.error("Verbindung getrennt"); + console.log("WebSocket error"); + wsState.closed = true; + }); + + ws.addEventListener("message", (e) => { + const data = JSON.parse(e.data); + if (data.type === "hello") { + wsState.connected = true; + toast.success("Verbindung hergestellt"); + } + console.log("Received message:", data); + eventTarget.dispatchEvent(new CustomEvent(data.type, { detail: data })); + }); +} \ No newline at end of file diff --git a/web/src/routes/(manage)/+layout.svelte b/web/src/routes/(manage)/+layout.svelte new file mode 100644 index 0000000..0af1289 --- /dev/null +++ b/web/src/routes/(manage)/+layout.svelte @@ -0,0 +1,92 @@ + + + + + + +{#if wsState.closed} + +{/if} + +{#if !wsState.connected && !wsState.closed} +
    + + +

    Verbindung herstellen...

    +
    +{/if} + + + + + + + +
    + + + +
    +
    + {@render children?.()} +
    + +
    +
    + + diff --git a/web/src/routes/(manage)/+page.svelte b/web/src/routes/(manage)/+page.svelte new file mode 100644 index 0000000..b40d4f2 --- /dev/null +++ b/web/src/routes/(manage)/+page.svelte @@ -0,0 +1,43 @@ + + +

    Willkommen zu NextUp!

    +Sie werden zu Ihrem Raum weitergeleitet. +{#if noRoom} + Kein Raum verfügbar +{/if} \ No newline at end of file diff --git a/web/src/routes/(manage)/admin/+page.svelte b/web/src/routes/(manage)/admin/+page.svelte new file mode 100644 index 0000000..1041f24 --- /dev/null +++ b/web/src/routes/(manage)/admin/+page.svelte @@ -0,0 +1 @@ +ADMIN \ No newline at end of file diff --git a/web/src/routes/(manage)/entry/+page.svelte b/web/src/routes/(manage)/entry/+page.svelte new file mode 100644 index 0000000..0bf63bb --- /dev/null +++ b/web/src/routes/(manage)/entry/+page.svelte @@ -0,0 +1,120 @@ + + + diff --git a/web/src/routes/(manage)/log/+page.svelte b/web/src/routes/(manage)/log/+page.svelte new file mode 100644 index 0000000..39fe9ab --- /dev/null +++ b/web/src/routes/(manage)/log/+page.svelte @@ -0,0 +1,68 @@ + + +

    Log

    + + + + + Ticketnr. + Zeit + Aktion + Raum + + + + {#each logs as log} + + {log.ticket} + {new Date(log.time).toLocaleString("de-de")} + + {@const Icon = icons[log.action]} + + {text[log.action]} + + {log.room} + + {/each} + + \ No newline at end of file diff --git a/web/src/routes/(manage)/room/[slug]/+page.svelte b/web/src/routes/(manage)/room/[slug]/+page.svelte new file mode 100644 index 0000000..e831c45 --- /dev/null +++ b/web/src/routes/(manage)/room/[slug]/+page.svelte @@ -0,0 +1,135 @@ + + +

    Raum {slug}

    +
    + { + isInvalid = false; + if (e.key === "Enter") { + submit(); + } + }}> + {#snippet children({ cells })} + + {#each cells.slice(0, 3) as cell} + + {/each} + + {/snippet} + + + + +
    + +{#if currentTicket !== ""} +
    + Jetzt Aufgerufen: + {currentTicket} +
    +{/if} + + \ No newline at end of file diff --git a/web/src/routes/(manage)/ticket/[ticket]/+page.svelte b/web/src/routes/(manage)/ticket/[ticket]/+page.svelte new file mode 100644 index 0000000..7b01902 --- /dev/null +++ b/web/src/routes/(manage)/ticket/[ticket]/+page.svelte @@ -0,0 +1,100 @@ + + +

    Ticket {ticket} {ticketStatus(ticketInfo.status)}

    + +Geöffnet {timeAgo(ticketInfo.created)} + +{#if ticketInfo.room} + Aufgerufen in: {ticketInfo.room} +{/if} +{#if ticketInfo.calledDate} + Aufgerufen {timeAgo(ticketInfo.calledDate)} +{/if} + +{#if ticketInfo.status != "done"} + +{:else} + +{/if} diff --git a/web/src/routes/display/+layout.svelte b/web/src/routes/display/+layout.svelte new file mode 100644 index 0000000..00d0166 --- /dev/null +++ b/web/src/routes/display/+layout.svelte @@ -0,0 +1,49 @@ + + +{#if wsState.closed} + +{/if} + +{#if !wsState.connected && !wsState.closed} +
    + + +

    Verbindung herstellen...

    +
    +{/if} + +{@render children?.()} + + diff --git a/web/src/routes/display/+page.svelte b/web/src/routes/display/+page.svelte new file mode 100644 index 0000000..94f411c --- /dev/null +++ b/web/src/routes/display/+page.svelte @@ -0,0 +1,66 @@ + + +
    +

    Aufruf

    + {#if connected} + + + + + + + {#each calls as call (call.num)} + + + + + {/each} + +
    TicketRaum
    {call.num}{call.ticket.status === "no-show" ? "Empfang " + (call.num.startsWith("A") ? "1" : "2") : call.ticket.room}
    + {/if} +
    + + diff --git a/web/static/robots.txt b/web/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/web/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/web/svelte.config.js b/web/svelte.config.js new file mode 100644 index 0000000..e0a641e --- /dev/null +++ b/web/svelte.config.js @@ -0,0 +1,18 @@ +import adapter from '@sveltejs/adapter-node'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://svelte.dev/docs/kit/integrations + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://svelte.dev/docs/kit/adapters for more information about adapters. + adapter: adapter() + } +}; + +export default config; diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..a5567ee --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/web/vite.config.ts b/web/vite.config.ts new file mode 100644 index 0000000..2d35c4f --- /dev/null +++ b/web/vite.config.ts @@ -0,0 +1,7 @@ +import tailwindcss from '@tailwindcss/vite'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [tailwindcss(), sveltekit()] +});