From c35e0716af25e500ea1796f4375619af195afa59 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Sat, 2 Aug 2025 13:28:10 +0200 Subject: [PATCH] Final commit --- README.md | 126 +- backend/.DS_Store | Bin 0 -> 6148 bytes backend/.gitignore | 1 + backend/.zed/settings.json | 40 + backend/.zed/tasks.json | 16 + backend/Dockerfile | 13 + backend/ai-selector.ts | 48 + backend/ai-suggestion.ts | 65 + backend/deno.json | 0 backend/deno.lock | 209 + backend/generate-schemas.sh | 21 + backend/main.ts | 190 + backend/routes/get-new-suggest.ts | 15 + backend/routes/get-song.ts | 18 + backend/routes/get-track.ts | 87 + backend/routes/get-video.ts | 9 + backend/schemas/AiSnewuggestionOutput.json | 30 + backend/schemas/AiSuggestionsOutput.json | 56 + backend/spotify-api.ts | 136 + backend/types/ai.ts | 19 + backend/types/api.ts | 47 + backend/types/spotify.ts | 97 + backend/types/youtube.ts | 4 + backend/youtube-api.ts | 46 + frontend/.gitignore | 23 + frontend/.npmrc | 1 + frontend/.prettierignore | 9 + frontend/.prettierrc | 17 + frontend/.zed/tasks.json | 6 + frontend/Dockerfile | 21 + frontend/components.json | 16 + frontend/cspell.json | 6 + frontend/package-lock.json | 4740 +++++++++++++++++ frontend/package.json | 53 + frontend/src/app.css | 128 + frontend/src/app.d.ts | 13 + frontend/src/app.html | 14 + frontend/src/lib/assets/favicon.svg | 1 + .../src/lib/components/app-sidebar.svelte | 171 + .../src/lib/components/search-form.svelte | 21 + .../ui/accordion/accordion-content.svelte | 22 + .../ui/accordion/accordion-item.svelte | 17 + .../ui/accordion/accordion-trigger.svelte | 32 + .../components/ui/accordion/accordion.svelte | 16 + .../src/lib/components/ui/accordion/index.ts | 16 + .../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 + .../ui/alert/alert-description.svelte | 23 + .../components/ui/alert/alert-title.svelte | 20 + .../src/lib/components/ui/alert/alert.svelte | 44 + frontend/src/lib/components/ui/alert/index.ts | 14 + .../ui/aspect-ratio/aspect-ratio.svelte | 7 + .../lib/components/ui/aspect-ratio/index.ts | 3 + .../ui/avatar/avatar-fallback.svelte | 17 + .../components/ui/avatar/avatar-image.svelte | 17 + .../lib/components/ui/avatar/avatar.svelte | 19 + .../src/lib/components/ui/avatar/index.ts | 13 + .../src/lib/components/ui/badge/badge.svelte | 50 + frontend/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 + .../src/lib/components/ui/breadcrumb/index.ts | 25 + .../lib/components/ui/button/button.svelte | 80 + .../src/lib/components/ui/button/index.ts | 17 + .../ui/calendar/calendar-caption.svelte | 76 + .../ui/calendar/calendar-cell.svelte | 19 + .../ui/calendar/calendar-day.svelte | 35 + .../ui/calendar/calendar-grid-body.svelte | 12 + .../ui/calendar/calendar-grid-head.svelte | 12 + .../ui/calendar/calendar-grid-row.svelte | 12 + .../ui/calendar/calendar-grid.svelte | 16 + .../ui/calendar/calendar-head-cell.svelte | 19 + .../ui/calendar/calendar-header.svelte | 19 + .../ui/calendar/calendar-heading.svelte | 16 + .../ui/calendar/calendar-month-select.svelte | 44 + .../ui/calendar/calendar-month.svelte | 15 + .../ui/calendar/calendar-months.svelte | 19 + .../ui/calendar/calendar-nav.svelte | 19 + .../ui/calendar/calendar-next-button.svelte | 31 + .../ui/calendar/calendar-prev-button.svelte | 31 + .../ui/calendar/calendar-year-select.svelte | 43 + .../components/ui/calendar/calendar.svelte | 115 + .../src/lib/components/ui/calendar/index.ts | 40 + .../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 + .../src/lib/components/ui/card/card.svelte | 23 + frontend/src/lib/components/ui/card/index.ts | 25 + .../ui/carousel/carousel-content.svelte | 43 + .../ui/carousel/carousel-item.svelte | 30 + .../ui/carousel/carousel-next.svelte | 38 + .../ui/carousel/carousel-previous.svelte | 38 + .../components/ui/carousel/carousel.svelte | 93 + .../src/lib/components/ui/carousel/context.ts | 58 + .../src/lib/components/ui/carousel/index.ts | 19 + .../ui/chart/chart-container.svelte | 80 + .../components/ui/chart/chart-style.svelte | 37 + .../components/ui/chart/chart-tooltip.svelte | 159 + .../lib/components/ui/chart/chart-utils.ts | 66 + frontend/src/lib/components/ui/chart/index.ts | 6 + .../components/ui/checkbox/checkbox.svelte | 36 + .../src/lib/components/ui/checkbox/index.ts | 6 + .../ui/collapsible/collapsible-content.svelte | 7 + .../ui/collapsible/collapsible-trigger.svelte | 7 + .../ui/collapsible/collapsible.svelte | 11 + .../lib/components/ui/collapsible/index.ts | 13 + .../ui/command/command-dialog.svelte | 40 + .../ui/command/command-empty.svelte | 17 + .../ui/command/command-group.svelte | 32 + .../ui/command/command-input.svelte | 26 + .../components/ui/command/command-item.svelte | 20 + .../ui/command/command-link-item.svelte | 20 + .../components/ui/command/command-list.svelte | 17 + .../ui/command/command-separator.svelte | 17 + .../ui/command/command-shortcut.svelte | 20 + .../lib/components/ui/command/command.svelte | 22 + .../src/lib/components/ui/command/index.ts | 40 + .../context-menu-checkbox-item.svelte | 38 + .../context-menu/context-menu-content.svelte | 25 + .../context-menu-group-heading.svelte | 21 + .../ui/context-menu/context-menu-group.svelte | 7 + .../ui/context-menu/context-menu-item.svelte | 27 + .../ui/context-menu/context-menu-label.svelte | 24 + .../context-menu-radio-group.svelte | 16 + .../context-menu-radio-item.svelte | 31 + .../context-menu-separator.svelte | 17 + .../context-menu/context-menu-shortcut.svelte | 20 + .../context-menu-sub-content.svelte | 20 + .../context-menu-sub-trigger.svelte | 29 + .../context-menu/context-menu-trigger.svelte | 7 + .../lib/components/ui/context-menu/index.ts | 51 + .../ui/data-table/data-table.svelte.ts | 141 + .../ui/data-table/flex-render.svelte | 36 + .../src/lib/components/ui/data-table/index.ts | 3 + .../ui/data-table/render-helpers.ts | 111 + .../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 + .../src/lib/components/ui/dialog/index.ts | 37 + .../components/ui/drawer/drawer-close.svelte | 7 + .../ui/drawer/drawer-content.svelte | 37 + .../ui/drawer/drawer-description.svelte | 17 + .../components/ui/drawer/drawer-footer.svelte | 20 + .../components/ui/drawer/drawer-header.svelte | 20 + .../components/ui/drawer/drawer-nested.svelte | 12 + .../ui/drawer/drawer-overlay.svelte | 20 + .../components/ui/drawer/drawer-title.svelte | 17 + .../ui/drawer/drawer-trigger.svelte | 7 + .../lib/components/ui/drawer/drawer.svelte | 12 + .../src/lib/components/ui/drawer/index.ts | 41 + .../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 + .../lib/components/ui/form/form-button.svelte | 7 + .../ui/form/form-description.svelte | 17 + .../ui/form/form-element-field.svelte | 24 + .../ui/form/form-field-errors.svelte | 30 + .../lib/components/ui/form/form-field.svelte | 29 + .../components/ui/form/form-fieldset.svelte | 15 + .../lib/components/ui/form/form-label.svelte | 24 + .../lib/components/ui/form/form-legend.svelte | 16 + frontend/src/lib/components/ui/form/index.ts | 33 + .../ui/hover-card/hover-card-content.svelte | 29 + .../ui/hover-card/hover-card-trigger.svelte | 7 + .../src/lib/components/ui/hover-card/index.ts | 14 + .../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 + frontend/src/lib/components/ui/input/index.ts | 7 + .../src/lib/components/ui/input/input.svelte | 51 + frontend/src/lib/components/ui/label/index.ts | 7 + .../src/lib/components/ui/label/label.svelte | 20 + .../src/lib/components/ui/menubar/index.ts | 54 + .../ui/menubar/menubar-checkbox-item.svelte | 41 + .../ui/menubar/menubar-content.svelte | 33 + .../ui/menubar/menubar-group-heading.svelte | 22 + .../ui/menubar/menubar-group.svelte | 12 + .../components/ui/menubar/menubar-item.svelte | 27 + .../ui/menubar/menubar-label.svelte | 25 + .../ui/menubar/menubar-radio-item.svelte | 31 + .../ui/menubar/menubar-separator.svelte | 17 + .../ui/menubar/menubar-shortcut.svelte | 20 + .../ui/menubar/menubar-sub-content.svelte | 20 + .../ui/menubar/menubar-sub-trigger.svelte | 29 + .../ui/menubar/menubar-trigger.svelte | 20 + .../lib/components/ui/menubar/menubar.svelte | 20 + .../components/ui/navigation-menu/index.ts | 28 + .../navigation-menu-content.svelte | 21 + .../navigation-menu-indicator.svelte | 22 + .../navigation-menu-item.svelte | 17 + .../navigation-menu-link.svelte | 20 + .../navigation-menu-list.svelte | 17 + .../navigation-menu-trigger.svelte | 34 + .../navigation-menu-viewport.svelte | 22 + .../ui/navigation-menu/navigation-menu.svelte | 32 + .../src/lib/components/ui/pagination/index.ts | 25 + .../ui/pagination/pagination-content.svelte | 20 + .../ui/pagination/pagination-ellipsis.svelte | 22 + .../ui/pagination/pagination-item.svelte | 14 + .../ui/pagination/pagination-link.svelte | 39 + .../pagination/pagination-next-button.svelte | 33 + .../pagination/pagination-prev-button.svelte | 33 + .../ui/pagination/pagination.svelte | 28 + .../src/lib/components/ui/popover/index.ts | 17 + .../ui/popover/popover-content.svelte | 29 + .../ui/popover/popover-trigger.svelte | 17 + .../src/lib/components/ui/progress/index.ts | 7 + .../components/ui/progress/progress.svelte | 27 + .../lib/components/ui/radio-group/index.ts | 10 + .../ui/radio-group/radio-group-item.svelte | 31 + .../ui/radio-group/radio-group.svelte | 19 + .../lib/components/ui/range-calendar/index.ts | 42 + .../range-calendar-caption.svelte | 76 + .../range-calendar/range-calendar-cell.svelte | 19 + .../range-calendar/range-calendar-day.svelte | 39 + .../range-calendar-grid-row.svelte | 12 + .../range-calendar/range-calendar-grid.svelte | 16 + .../range-calendar-head-cell.svelte | 19 + .../range-calendar-header.svelte | 19 + .../range-calendar-heading.svelte | 16 + .../range-calendar-month-select.svelte | 44 + .../range-calendar-month.svelte | 15 + .../range-calendar-months.svelte | 19 + .../range-calendar/range-calendar-nav.svelte | 19 + .../range-calendar-next-button.svelte | 31 + .../range-calendar-prev-button.svelte | 31 + .../range-calendar-year-select.svelte | 43 + .../ui/range-calendar/range-calendar.svelte | 112 + .../src/lib/components/ui/resizable/index.ts | 13 + .../ui/resizable/resizable-handle.svelte | 30 + .../ui/resizable/resizable-pane-group.svelte | 20 + .../lib/components/ui/scroll-area/index.ts | 10 + .../scroll-area/scroll-area-scrollbar.svelte | 31 + .../ui/scroll-area/scroll-area.svelte | 40 + .../src/lib/components/ui/select/index.ts | 37 + .../ui/select/select-content.svelte | 40 + .../ui/select/select-group-heading.svelte | 21 + .../components/ui/select/select-group.svelte | 7 + .../components/ui/select/select-item.svelte | 38 + .../components/ui/select/select-label.svelte | 20 + .../select/select-scroll-down-button.svelte | 20 + .../ui/select/select-scroll-up-button.svelte | 20 + .../ui/select/select-separator.svelte | 18 + .../ui/select/select-trigger.svelte | 29 + .../src/lib/components/ui/separator/index.ts | 7 + .../components/ui/separator/separator.svelte | 20 + frontend/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 + .../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 + .../src/lib/components/ui/skeleton/index.ts | 7 + .../components/ui/skeleton/skeleton.svelte | 17 + .../src/lib/components/ui/slider/index.ts | 7 + .../lib/components/ui/slider/slider.svelte | 52 + .../src/lib/components/ui/sonner/index.ts | 1 + .../lib/components/ui/sonner/sonner.svelte | 13 + .../src/lib/components/ui/switch/index.ts | 7 + .../lib/components/ui/switch/switch.svelte | 29 + frontend/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 + .../src/lib/components/ui/table/table.svelte | 22 + frontend/src/lib/components/ui/tabs/index.ts | 16 + .../components/ui/tabs/tabs-content.svelte | 17 + .../lib/components/ui/tabs/tabs-list.svelte | 20 + .../components/ui/tabs/tabs-trigger.svelte | 20 + .../src/lib/components/ui/tabs/tabs.svelte | 19 + .../src/lib/components/ui/textarea/index.ts | 7 + .../components/ui/textarea/textarea.svelte | 22 + .../lib/components/ui/toggle-group/index.ts | 10 + .../ui/toggle-group/toggle-group-item.svelte | 34 + .../ui/toggle-group/toggle-group.svelte | 47 + .../src/lib/components/ui/toggle/index.ts | 13 + .../lib/components/ui/toggle/toggle.svelte | 52 + .../src/lib/components/ui/tooltip/index.ts | 21 + .../ui/tooltip/tooltip-content.svelte | 47 + .../ui/tooltip/tooltip-trigger.svelte | 7 + .../lib/components/version-switcher.svelte | 48 + frontend/src/lib/database.types.ts | 239 + frontend/src/lib/env.ts | 26 + frontend/src/lib/hooks/is-mobile.svelte.ts | 9 + frontend/src/lib/index.ts | 1 + frontend/src/lib/spotify.ts | 100 + frontend/src/lib/stores.ts | 58 + frontend/src/lib/supabase.ts | 51 + frontend/src/lib/utils.ts | 13 + frontend/src/routes/+layout.svelte | 21 + frontend/src/routes/+layout.ts | 1 + frontend/src/routes/+page.svelte | 0 frontend/src/routes/+page.ts | 3 + frontend/src/routes/create/+page.svelte | 127 + .../src/routes/space/[space]/+layout.svelte | 155 + frontend/src/routes/space/[space]/+layout.ts | 37 + .../src/routes/space/[space]/+page.svelte | 387 ++ .../src/routes/space/[space]/AddSongs.svelte | 141 + frontend/src/routes/space/[space]/Song.svelte | 72 + .../routes/space/[space]/VoteCounter.svelte | 130 + .../src/routes/space/[space]/functions.ts | 180 + frontend/static/robots.txt | 3 + frontend/svelte.config.js | 21 + frontend/tsconfig.json | 19 + frontend/vite.config.ts | 11 + 372 files changed, 16591 insertions(+), 1 deletion(-) create mode 100644 backend/.DS_Store create mode 100644 backend/.gitignore create mode 100644 backend/.zed/settings.json create mode 100644 backend/.zed/tasks.json create mode 100644 backend/Dockerfile create mode 100644 backend/ai-selector.ts create mode 100644 backend/ai-suggestion.ts create mode 100644 backend/deno.json create mode 100644 backend/deno.lock create mode 100755 backend/generate-schemas.sh create mode 100644 backend/main.ts create mode 100644 backend/routes/get-new-suggest.ts create mode 100644 backend/routes/get-song.ts create mode 100644 backend/routes/get-track.ts create mode 100644 backend/routes/get-video.ts create mode 100644 backend/schemas/AiSnewuggestionOutput.json create mode 100644 backend/schemas/AiSuggestionsOutput.json create mode 100644 backend/spotify-api.ts create mode 100644 backend/types/ai.ts create mode 100644 backend/types/api.ts create mode 100644 backend/types/spotify.ts create mode 100644 backend/types/youtube.ts create mode 100644 backend/youtube-api.ts create mode 100644 frontend/.gitignore create mode 100644 frontend/.npmrc create mode 100644 frontend/.prettierignore create mode 100644 frontend/.prettierrc create mode 100644 frontend/.zed/tasks.json create mode 100644 frontend/Dockerfile create mode 100644 frontend/components.json create mode 100644 frontend/cspell.json create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/src/app.css create mode 100644 frontend/src/app.d.ts create mode 100644 frontend/src/app.html create mode 100644 frontend/src/lib/assets/favicon.svg create mode 100644 frontend/src/lib/components/app-sidebar.svelte create mode 100644 frontend/src/lib/components/search-form.svelte create mode 100644 frontend/src/lib/components/ui/accordion/accordion-content.svelte create mode 100644 frontend/src/lib/components/ui/accordion/accordion-item.svelte create mode 100644 frontend/src/lib/components/ui/accordion/accordion-trigger.svelte create mode 100644 frontend/src/lib/components/ui/accordion/accordion.svelte create mode 100644 frontend/src/lib/components/ui/accordion/index.ts create mode 100644 frontend/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte create mode 100644 frontend/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte create mode 100644 frontend/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte create mode 100644 frontend/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte create mode 100644 frontend/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte create mode 100644 frontend/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte create mode 100644 frontend/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte create mode 100644 frontend/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte create mode 100644 frontend/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte create mode 100644 frontend/src/lib/components/ui/alert-dialog/index.ts create mode 100644 frontend/src/lib/components/ui/alert/alert-description.svelte create mode 100644 frontend/src/lib/components/ui/alert/alert-title.svelte create mode 100644 frontend/src/lib/components/ui/alert/alert.svelte create mode 100644 frontend/src/lib/components/ui/alert/index.ts create mode 100644 frontend/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte create mode 100644 frontend/src/lib/components/ui/aspect-ratio/index.ts create mode 100644 frontend/src/lib/components/ui/avatar/avatar-fallback.svelte create mode 100644 frontend/src/lib/components/ui/avatar/avatar-image.svelte create mode 100644 frontend/src/lib/components/ui/avatar/avatar.svelte create mode 100644 frontend/src/lib/components/ui/avatar/index.ts create mode 100644 frontend/src/lib/components/ui/badge/badge.svelte create mode 100644 frontend/src/lib/components/ui/badge/index.ts create mode 100644 frontend/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte create mode 100644 frontend/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte create mode 100644 frontend/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte create mode 100644 frontend/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte create mode 100644 frontend/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte create mode 100644 frontend/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte create mode 100644 frontend/src/lib/components/ui/breadcrumb/breadcrumb.svelte create mode 100644 frontend/src/lib/components/ui/breadcrumb/index.ts create mode 100644 frontend/src/lib/components/ui/button/button.svelte create mode 100644 frontend/src/lib/components/ui/button/index.ts create mode 100644 frontend/src/lib/components/ui/calendar/calendar-caption.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-cell.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-day.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-grid-body.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-grid-head.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-grid-row.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-grid.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-head-cell.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-header.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-heading.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-month-select.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-month.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-months.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-nav.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-next-button.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-prev-button.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar-year-select.svelte create mode 100644 frontend/src/lib/components/ui/calendar/calendar.svelte create mode 100644 frontend/src/lib/components/ui/calendar/index.ts create mode 100644 frontend/src/lib/components/ui/card/card-action.svelte create mode 100644 frontend/src/lib/components/ui/card/card-content.svelte create mode 100644 frontend/src/lib/components/ui/card/card-description.svelte create mode 100644 frontend/src/lib/components/ui/card/card-footer.svelte create mode 100644 frontend/src/lib/components/ui/card/card-header.svelte create mode 100644 frontend/src/lib/components/ui/card/card-title.svelte create mode 100644 frontend/src/lib/components/ui/card/card.svelte create mode 100644 frontend/src/lib/components/ui/card/index.ts create mode 100644 frontend/src/lib/components/ui/carousel/carousel-content.svelte create mode 100644 frontend/src/lib/components/ui/carousel/carousel-item.svelte create mode 100644 frontend/src/lib/components/ui/carousel/carousel-next.svelte create mode 100644 frontend/src/lib/components/ui/carousel/carousel-previous.svelte create mode 100644 frontend/src/lib/components/ui/carousel/carousel.svelte create mode 100644 frontend/src/lib/components/ui/carousel/context.ts create mode 100644 frontend/src/lib/components/ui/carousel/index.ts create mode 100644 frontend/src/lib/components/ui/chart/chart-container.svelte create mode 100644 frontend/src/lib/components/ui/chart/chart-style.svelte create mode 100644 frontend/src/lib/components/ui/chart/chart-tooltip.svelte create mode 100644 frontend/src/lib/components/ui/chart/chart-utils.ts create mode 100644 frontend/src/lib/components/ui/chart/index.ts create mode 100644 frontend/src/lib/components/ui/checkbox/checkbox.svelte create mode 100644 frontend/src/lib/components/ui/checkbox/index.ts create mode 100644 frontend/src/lib/components/ui/collapsible/collapsible-content.svelte create mode 100644 frontend/src/lib/components/ui/collapsible/collapsible-trigger.svelte create mode 100644 frontend/src/lib/components/ui/collapsible/collapsible.svelte create mode 100644 frontend/src/lib/components/ui/collapsible/index.ts create mode 100644 frontend/src/lib/components/ui/command/command-dialog.svelte create mode 100644 frontend/src/lib/components/ui/command/command-empty.svelte create mode 100644 frontend/src/lib/components/ui/command/command-group.svelte create mode 100644 frontend/src/lib/components/ui/command/command-input.svelte create mode 100644 frontend/src/lib/components/ui/command/command-item.svelte create mode 100644 frontend/src/lib/components/ui/command/command-link-item.svelte create mode 100644 frontend/src/lib/components/ui/command/command-list.svelte create mode 100644 frontend/src/lib/components/ui/command/command-separator.svelte create mode 100644 frontend/src/lib/components/ui/command/command-shortcut.svelte create mode 100644 frontend/src/lib/components/ui/command/command.svelte create mode 100644 frontend/src/lib/components/ui/command/index.ts create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-checkbox-item.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-content.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-group-heading.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-group.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-item.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-label.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-radio-group.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-radio-item.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-separator.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-shortcut.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-sub-content.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/context-menu-trigger.svelte create mode 100644 frontend/src/lib/components/ui/context-menu/index.ts create mode 100644 frontend/src/lib/components/ui/data-table/data-table.svelte.ts create mode 100644 frontend/src/lib/components/ui/data-table/flex-render.svelte create mode 100644 frontend/src/lib/components/ui/data-table/index.ts create mode 100644 frontend/src/lib/components/ui/data-table/render-helpers.ts create mode 100644 frontend/src/lib/components/ui/dialog/dialog-close.svelte create mode 100644 frontend/src/lib/components/ui/dialog/dialog-content.svelte create mode 100644 frontend/src/lib/components/ui/dialog/dialog-description.svelte create mode 100644 frontend/src/lib/components/ui/dialog/dialog-footer.svelte create mode 100644 frontend/src/lib/components/ui/dialog/dialog-header.svelte create mode 100644 frontend/src/lib/components/ui/dialog/dialog-overlay.svelte create mode 100644 frontend/src/lib/components/ui/dialog/dialog-title.svelte create mode 100644 frontend/src/lib/components/ui/dialog/dialog-trigger.svelte create mode 100644 frontend/src/lib/components/ui/dialog/index.ts create mode 100644 frontend/src/lib/components/ui/drawer/drawer-close.svelte create mode 100644 frontend/src/lib/components/ui/drawer/drawer-content.svelte create mode 100644 frontend/src/lib/components/ui/drawer/drawer-description.svelte create mode 100644 frontend/src/lib/components/ui/drawer/drawer-footer.svelte create mode 100644 frontend/src/lib/components/ui/drawer/drawer-header.svelte create mode 100644 frontend/src/lib/components/ui/drawer/drawer-nested.svelte create mode 100644 frontend/src/lib/components/ui/drawer/drawer-overlay.svelte create mode 100644 frontend/src/lib/components/ui/drawer/drawer-title.svelte create mode 100644 frontend/src/lib/components/ui/drawer/drawer-trigger.svelte create mode 100644 frontend/src/lib/components/ui/drawer/drawer.svelte create mode 100644 frontend/src/lib/components/ui/drawer/index.ts create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte create mode 100644 frontend/src/lib/components/ui/dropdown-menu/index.ts create mode 100644 frontend/src/lib/components/ui/form/form-button.svelte create mode 100644 frontend/src/lib/components/ui/form/form-description.svelte create mode 100644 frontend/src/lib/components/ui/form/form-element-field.svelte create mode 100644 frontend/src/lib/components/ui/form/form-field-errors.svelte create mode 100644 frontend/src/lib/components/ui/form/form-field.svelte create mode 100644 frontend/src/lib/components/ui/form/form-fieldset.svelte create mode 100644 frontend/src/lib/components/ui/form/form-label.svelte create mode 100644 frontend/src/lib/components/ui/form/form-legend.svelte create mode 100644 frontend/src/lib/components/ui/form/index.ts create mode 100644 frontend/src/lib/components/ui/hover-card/hover-card-content.svelte create mode 100644 frontend/src/lib/components/ui/hover-card/hover-card-trigger.svelte create mode 100644 frontend/src/lib/components/ui/hover-card/index.ts create mode 100644 frontend/src/lib/components/ui/input-otp/index.ts create mode 100644 frontend/src/lib/components/ui/input-otp/input-otp-group.svelte create mode 100644 frontend/src/lib/components/ui/input-otp/input-otp-separator.svelte create mode 100644 frontend/src/lib/components/ui/input-otp/input-otp-slot.svelte create mode 100644 frontend/src/lib/components/ui/input-otp/input-otp.svelte create mode 100644 frontend/src/lib/components/ui/input/index.ts create mode 100644 frontend/src/lib/components/ui/input/input.svelte create mode 100644 frontend/src/lib/components/ui/label/index.ts create mode 100644 frontend/src/lib/components/ui/label/label.svelte create mode 100644 frontend/src/lib/components/ui/menubar/index.ts create mode 100644 frontend/src/lib/components/ui/menubar/menubar-checkbox-item.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-content.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-group-heading.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-group.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-item.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-label.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-radio-item.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-separator.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-shortcut.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-sub-content.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-sub-trigger.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar-trigger.svelte create mode 100644 frontend/src/lib/components/ui/menubar/menubar.svelte create mode 100644 frontend/src/lib/components/ui/navigation-menu/index.ts create mode 100644 frontend/src/lib/components/ui/navigation-menu/navigation-menu-content.svelte create mode 100644 frontend/src/lib/components/ui/navigation-menu/navigation-menu-indicator.svelte create mode 100644 frontend/src/lib/components/ui/navigation-menu/navigation-menu-item.svelte create mode 100644 frontend/src/lib/components/ui/navigation-menu/navigation-menu-link.svelte create mode 100644 frontend/src/lib/components/ui/navigation-menu/navigation-menu-list.svelte create mode 100644 frontend/src/lib/components/ui/navigation-menu/navigation-menu-trigger.svelte create mode 100644 frontend/src/lib/components/ui/navigation-menu/navigation-menu-viewport.svelte create mode 100644 frontend/src/lib/components/ui/navigation-menu/navigation-menu.svelte create mode 100644 frontend/src/lib/components/ui/pagination/index.ts create mode 100644 frontend/src/lib/components/ui/pagination/pagination-content.svelte create mode 100644 frontend/src/lib/components/ui/pagination/pagination-ellipsis.svelte create mode 100644 frontend/src/lib/components/ui/pagination/pagination-item.svelte create mode 100644 frontend/src/lib/components/ui/pagination/pagination-link.svelte create mode 100644 frontend/src/lib/components/ui/pagination/pagination-next-button.svelte create mode 100644 frontend/src/lib/components/ui/pagination/pagination-prev-button.svelte create mode 100644 frontend/src/lib/components/ui/pagination/pagination.svelte create mode 100644 frontend/src/lib/components/ui/popover/index.ts create mode 100644 frontend/src/lib/components/ui/popover/popover-content.svelte create mode 100644 frontend/src/lib/components/ui/popover/popover-trigger.svelte create mode 100644 frontend/src/lib/components/ui/progress/index.ts create mode 100644 frontend/src/lib/components/ui/progress/progress.svelte create mode 100644 frontend/src/lib/components/ui/radio-group/index.ts create mode 100644 frontend/src/lib/components/ui/radio-group/radio-group-item.svelte create mode 100644 frontend/src/lib/components/ui/radio-group/radio-group.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/index.ts create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-caption.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-cell.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-day.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-grid.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-header.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-heading.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-month-select.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-month.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-months.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-nav.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar-year-select.svelte create mode 100644 frontend/src/lib/components/ui/range-calendar/range-calendar.svelte create mode 100644 frontend/src/lib/components/ui/resizable/index.ts create mode 100644 frontend/src/lib/components/ui/resizable/resizable-handle.svelte create mode 100644 frontend/src/lib/components/ui/resizable/resizable-pane-group.svelte create mode 100644 frontend/src/lib/components/ui/scroll-area/index.ts create mode 100644 frontend/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte create mode 100644 frontend/src/lib/components/ui/scroll-area/scroll-area.svelte create mode 100644 frontend/src/lib/components/ui/select/index.ts create mode 100644 frontend/src/lib/components/ui/select/select-content.svelte create mode 100644 frontend/src/lib/components/ui/select/select-group-heading.svelte create mode 100644 frontend/src/lib/components/ui/select/select-group.svelte create mode 100644 frontend/src/lib/components/ui/select/select-item.svelte create mode 100644 frontend/src/lib/components/ui/select/select-label.svelte create mode 100644 frontend/src/lib/components/ui/select/select-scroll-down-button.svelte create mode 100644 frontend/src/lib/components/ui/select/select-scroll-up-button.svelte create mode 100644 frontend/src/lib/components/ui/select/select-separator.svelte create mode 100644 frontend/src/lib/components/ui/select/select-trigger.svelte create mode 100644 frontend/src/lib/components/ui/separator/index.ts create mode 100644 frontend/src/lib/components/ui/separator/separator.svelte create mode 100644 frontend/src/lib/components/ui/sheet/index.ts create mode 100644 frontend/src/lib/components/ui/sheet/sheet-close.svelte create mode 100644 frontend/src/lib/components/ui/sheet/sheet-content.svelte create mode 100644 frontend/src/lib/components/ui/sheet/sheet-description.svelte create mode 100644 frontend/src/lib/components/ui/sheet/sheet-footer.svelte create mode 100644 frontend/src/lib/components/ui/sheet/sheet-header.svelte create mode 100644 frontend/src/lib/components/ui/sheet/sheet-overlay.svelte create mode 100644 frontend/src/lib/components/ui/sheet/sheet-title.svelte create mode 100644 frontend/src/lib/components/ui/sheet/sheet-trigger.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/constants.ts create mode 100644 frontend/src/lib/components/ui/sidebar/context.svelte.ts create mode 100644 frontend/src/lib/components/ui/sidebar/index.ts create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-content.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-footer.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-group-action.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-group-content.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-group-label.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-group.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-header.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-input.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-inset.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-menu-action.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-menu-button.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-menu-item.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-menu.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-provider.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-rail.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-separator.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar-trigger.svelte create mode 100644 frontend/src/lib/components/ui/sidebar/sidebar.svelte create mode 100644 frontend/src/lib/components/ui/skeleton/index.ts create mode 100644 frontend/src/lib/components/ui/skeleton/skeleton.svelte create mode 100644 frontend/src/lib/components/ui/slider/index.ts create mode 100644 frontend/src/lib/components/ui/slider/slider.svelte create mode 100644 frontend/src/lib/components/ui/sonner/index.ts create mode 100644 frontend/src/lib/components/ui/sonner/sonner.svelte create mode 100644 frontend/src/lib/components/ui/switch/index.ts create mode 100644 frontend/src/lib/components/ui/switch/switch.svelte create mode 100644 frontend/src/lib/components/ui/table/index.ts create mode 100644 frontend/src/lib/components/ui/table/table-body.svelte create mode 100644 frontend/src/lib/components/ui/table/table-caption.svelte create mode 100644 frontend/src/lib/components/ui/table/table-cell.svelte create mode 100644 frontend/src/lib/components/ui/table/table-footer.svelte create mode 100644 frontend/src/lib/components/ui/table/table-head.svelte create mode 100644 frontend/src/lib/components/ui/table/table-header.svelte create mode 100644 frontend/src/lib/components/ui/table/table-row.svelte create mode 100644 frontend/src/lib/components/ui/table/table.svelte create mode 100644 frontend/src/lib/components/ui/tabs/index.ts create mode 100644 frontend/src/lib/components/ui/tabs/tabs-content.svelte create mode 100644 frontend/src/lib/components/ui/tabs/tabs-list.svelte create mode 100644 frontend/src/lib/components/ui/tabs/tabs-trigger.svelte create mode 100644 frontend/src/lib/components/ui/tabs/tabs.svelte create mode 100644 frontend/src/lib/components/ui/textarea/index.ts create mode 100644 frontend/src/lib/components/ui/textarea/textarea.svelte create mode 100644 frontend/src/lib/components/ui/toggle-group/index.ts create mode 100644 frontend/src/lib/components/ui/toggle-group/toggle-group-item.svelte create mode 100644 frontend/src/lib/components/ui/toggle-group/toggle-group.svelte create mode 100644 frontend/src/lib/components/ui/toggle/index.ts create mode 100644 frontend/src/lib/components/ui/toggle/toggle.svelte create mode 100644 frontend/src/lib/components/ui/tooltip/index.ts create mode 100644 frontend/src/lib/components/ui/tooltip/tooltip-content.svelte create mode 100644 frontend/src/lib/components/ui/tooltip/tooltip-trigger.svelte create mode 100644 frontend/src/lib/components/version-switcher.svelte create mode 100644 frontend/src/lib/database.types.ts create mode 100644 frontend/src/lib/env.ts create mode 100644 frontend/src/lib/hooks/is-mobile.svelte.ts create mode 100644 frontend/src/lib/index.ts create mode 100644 frontend/src/lib/spotify.ts create mode 100644 frontend/src/lib/stores.ts create mode 100644 frontend/src/lib/supabase.ts create mode 100644 frontend/src/lib/utils.ts create mode 100644 frontend/src/routes/+layout.svelte create mode 100644 frontend/src/routes/+layout.ts create mode 100644 frontend/src/routes/+page.svelte create mode 100644 frontend/src/routes/+page.ts create mode 100644 frontend/src/routes/create/+page.svelte create mode 100644 frontend/src/routes/space/[space]/+layout.svelte create mode 100644 frontend/src/routes/space/[space]/+layout.ts create mode 100644 frontend/src/routes/space/[space]/+page.svelte create mode 100644 frontend/src/routes/space/[space]/AddSongs.svelte create mode 100644 frontend/src/routes/space/[space]/Song.svelte create mode 100644 frontend/src/routes/space/[space]/VoteCounter.svelte create mode 100644 frontend/src/routes/space/[space]/functions.ts create mode 100644 frontend/static/robots.txt create mode 100644 frontend/svelte.config.js create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts diff --git a/README.md b/README.md index da89ddd..411385d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,126 @@ -# team-4 +# 🎵 Supa Tunes +**A collaborative music queue app built by Supa Duo** + +Turn any space into a shared music experience! Supa Tunes lets people vote on songs, add their favorites, and enjoy music together in real-time. + +## ✨ Features + +- 🎧 **Real-time collaborative queue** - Everyone can add songs and vote +- 🎵 **Spotify integration** - Play music directly through Spotify +- 🤖 **AI-powered suggestions** - Get song recommendations based on vibes and room themes +- 📱 **QR code sharing** - Easy room access via QR codes +- ⚡ **Live updates** - Real-time sync across all devices +- 🗳️ **Smart voting system** - Rate-limited voting to keep things fair +- 🎯 **Room rules** - Set the vibe with custom music rules + +## 🚀 Quick Start + +### Prerequisites + +- [Deno](https://deno.land/) (for backend) +- [Node.js](https://nodejs.org/) (for frontend) +- Spotify Premium account +- API keys for Spotify, YouTube, and Google Gemini + +### Backend Setup + +1. Navigate to the backend directory: + +```bash +cd backend +``` + +2. Create a `.env` file with your API keys: + +```env +PORT=8000 +GEMINI_API_KEY=your_gemini_api_key +SPOTIFY_CLIENT_ID=your_spotify_client_id +SPOTIFY_CLIENT_SECRET=your_spotify_client_secret +YT_API_KEY=your_youtube_api_key +``` + +3. Run with Docker: + +```bash +docker build -f Dockerfile -t supa-tunes-backend . +docker run --rm -p 8000:8000 supa-tunes-backend +``` + +Or run directly with Deno: + +```bash +deno run --allow-net --allow-env main.ts +``` + +### Frontend Setup + +1. Navigate to the frontend directory: + +```bash +cd frontend +``` + +2. Install dependencies: + +```bash +npm install +``` + +3. Start the development server: + +```bash +npm run dev +``` + +## 🎮 How to Use + +1. **Create a Room**: Connect your Spotify account and create a new music space +2. **Share the QR Code**: Others can scan to join your room +3. **Add Songs**: Search for music using natural language (e.g., "upbeat 80s vibes") +4. **Vote**: Everyone gets votes to influence what plays next +5. **Enjoy**: Music plays automatically based on the queue and votes! + +## 🛠️ Tech Stack + +**Backend (Deno/TypeScript)** + +- Deno runtime with TypeScript +- Google Gemini AI for music suggestions +- Spotify Web API integration +- YouTube API for video content +- RESTful API design + +**Frontend (SvelteKit)** + +- SvelteKit with TypeScript +- Supabase for real-time database +- Tailwind CSS for styling +- Real-time subscriptions for live updates + +**Infrastructure** + +- Docker containerization +- Supabase for database and real-time features +- Railway for deployment + +## 🎵 API Endpoints + +- `POST /music` - Search for songs with AI +- `POST /newSuggestions` - Get AI-powered room suggestions +- `POST /track` - Get track details by ID +- `POST /video` - Find YouTube videos for tracks +- `GET /health` - Health check + +## 🚀 Deployment + +The app is containerized and ready for deployment on Railway or any Docker-compatible platform. + +## 👥 Team Supa Duo + +Built with ❤️ by the Supa Duo team! + +## Thanks + +Special thanks to Konverto Lab for providing this challenge! diff --git a/backend/.DS_Store b/backend/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f0365368a1db8333673bc3071db9eae34ef97baf GIT binary patch literal 6148 zcmeHKJx{|h5IsXE5tUFzCgiCD15$qglBvSXPzi~xv_W)eNJZ%YQ+^u~>ObJmu<*_{ z#cmo#gy2rHUveM5mnS8TiAW8WMV}}mq7;KMIl#2VIL@PCJKn>^ir1Lk^@o$HoHsJX zAqt2Bzfl4E;?pTT(=8S8``hV(aPk=|t2`gisxf?N@2z+Ldb~Q1mRLAR$m<2a$+8adF<1ICNu?$l5RGcq%X~kkjv$0 z&1GYqkWG75%tteyJt*=-^QJ1@vp5>^IH}e=g8God_Kdi{;r?R(LjmmBY-!h_%%XrO zAPTG%;O~RRU^E_UhxXHf$sPfK3Eb9j&A${}qdgjrwL?T;%9R3Lsq#k*<;vmLKF@fp z9lCN-wlmi8os~bKDBB%=ZPQ5^hcb%-qCl*`uDi{6|G#KI|HnmgCkluHe@X$B { + const rulesText = rules && rules.trim().length > 0 + ? `Make sure that they follow the following set of rules: "${rules}".` + : ""; + const aiPrompt = ` + Based on this music request: "${userPrompt}", generate 15 suggestion for songs. + Make sure they are real, popular songs that exist on Spotify. + Make sure to follow the following set of costraints in order to suggest songs but bear in mind + to return an error only in extreme cases. Ypu can be a little open even if genres clash with eah other a little bit. + The rules are: "${rulesText}" + The type should be either succes or error. + Make the error as user friendly as possible ans concise, and don't make it happen too often. + `; + + const response = await ai.models.generateContent({ + model: "gemini-2.5-flash", + contents: aiPrompt, + config: { responseMimeType: "application/json", responseSchema: schema }, + }); + + const text = response.candidates?.at(0)?.content?.parts?.at(0)?.text; + if (!text) { + throw Error("Text not found"); + } + + return JSON.parse(text) as AiSuggestionsOutput; +} + +Deno.test("generateSongs basic", async () => { + const prompt = "Give me 5 songs by the queens"; + const rules = "Songs must vool"; + const songs = await generateSongs(prompt, rules); + + console.log(songs); + + assert(songs.type === "success"); +}); diff --git a/backend/ai-suggestion.ts b/backend/ai-suggestion.ts new file mode 100644 index 0000000..3ba6634 --- /dev/null +++ b/backend/ai-suggestion.ts @@ -0,0 +1,65 @@ +import { GoogleGenAI } from "npm:@google/genai@1.12.0"; +import { assert } from "https://deno.land/std@0.224.0/assert/mod.ts"; +import schema from "./schemas/AiSnewuggestionOutput.json" with { type: "json" }; +import { GetNewSuggestInput, GetNewSuggestOutput } from "./types/api.ts"; +import { AiSnewuggestionOutput } from "./types/ai.ts"; + +const ai = new GoogleGenAI({}); + +// Generate songs using AI +export async function generateVibes( + input: GetNewSuggestInput, +): Promise { + const { rules } = input; + let aiPrompt = ""; + let contextText = ""; + + if (input.type === "scratch-suggestion") { + contextText = + `The music request is for a place: "${input.room_name}". Your job is to find 15 songs that match the vibe of this place.`; + } else if (input.type === "from-songs-suggestion") { + const songList = input.songs.map((s, i) => `${i + 1}. ${s.song}`).join( + "\n", + ); + contextText = + `The music request is based on the last 10 songs:\n${songList}\nYour job is to 15 find songs that match the vibe of these songs.`; + } + + const rulesText = rules && rules.trim().length > 0 + ? `Make sure that they follow the following set of rules: "${rules}".` + : ""; + + aiPrompt = ` + ${contextText} + Make sure they are real, popular songs that exist on Spotify. + ${rulesText} + `; + + const response = await ai.models.generateContent({ + model: "gemini-2.5-flash", + contents: aiPrompt, + config: { responseMimeType: "application/json", responseSchema: schema }, + }); + + const text = response.candidates?.at(0)?.content?.parts?.at(0)?.text; + if (!text) { + throw Error("Text not found"); + } + + return JSON.parse(text) as AiSnewuggestionOutput; +} + +Deno.test("generateVibes with array of songs", async () => { + const input: GetNewSuggestInput = { + type: "from-songs-suggestion" as const, + rules: "The songs must be great for food", + songs: [ + { song: "Something - The Beatles" }, + { song: "Come Together - The Beatles" }, + { song: "Help! - The Beatles" }, + ], + }; + const result = await generateVibes(input); + + console.log(result); +}); diff --git a/backend/deno.json b/backend/deno.json new file mode 100644 index 0000000..e69de29 diff --git a/backend/deno.lock b/backend/deno.lock new file mode 100644 index 0000000..706e6bc --- /dev/null +++ b/backend/deno.lock @@ -0,0 +1,209 @@ +{ + "version": "5", + "specifiers": { + "jsr:@std/assert@*": "1.0.13", + "jsr:@std/internal@^1.0.6": "1.0.10", + "npm:@google/genai@1.12.0": "1.12.0" + }, + "jsr": { + "@std/assert@1.0.13": { + "integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29", + "dependencies": [ + "jsr:@std/internal" + ] + }, + "@std/internal@1.0.10": { + "integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7" + } + }, + "npm": { + "@google/genai@1.12.0": { + "integrity": "sha512-JBkQsULVexdM9zY4iXbm3A2dJ7El/hSPGCnxuRWPJNgeqcfYuyUnPTSy+I/v+MvTbz/occVmONSD6wn+17QLkg==", + "dependencies": [ + "google-auth-library", + "ws" + ] + }, + "agent-base@7.1.4": { + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==" + }, + "base64-js@1.5.1": { + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bignumber.js@9.3.1": { + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==" + }, + "buffer-equal-constant-time@1.0.1": { + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "debug@4.4.1": { + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dependencies": [ + "ms" + ] + }, + "ecdsa-sig-formatter@1.0.11": { + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": [ + "safe-buffer" + ] + }, + "extend@3.0.2": { + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "gaxios@6.7.1": { + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": [ + "extend", + "https-proxy-agent", + "is-stream", + "node-fetch", + "uuid" + ] + }, + "gcp-metadata@6.1.1": { + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "dependencies": [ + "gaxios", + "google-logging-utils", + "json-bigint" + ] + }, + "google-auth-library@9.15.1": { + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dependencies": [ + "base64-js", + "ecdsa-sig-formatter", + "gaxios", + "gcp-metadata", + "gtoken", + "jws" + ] + }, + "google-logging-utils@0.0.2": { + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==" + }, + "gtoken@7.1.0": { + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": [ + "gaxios", + "jws" + ] + }, + "https-proxy-agent@7.0.6": { + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": [ + "agent-base", + "debug" + ] + }, + "is-stream@2.0.1": { + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "json-bigint@1.0.0": { + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": [ + "bignumber.js" + ] + }, + "jwa@2.0.1": { + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dependencies": [ + "buffer-equal-constant-time", + "ecdsa-sig-formatter", + "safe-buffer" + ] + }, + "jws@4.0.0": { + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": [ + "jwa", + "safe-buffer" + ] + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node-fetch@2.7.0": { + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": [ + "whatwg-url" + ] + }, + "safe-buffer@5.2.1": { + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "tr46@0.0.3": { + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "uuid@9.0.1": { + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "bin": true + }, + "webidl-conversions@3.0.1": { + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url@5.0.0": { + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": [ + "tr46", + "webidl-conversions" + ] + }, + "ws@8.18.3": { + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==" + } + }, + "remote": { + "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", + "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", + "https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293", + "https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7", + "https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74", + "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd", + "https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff", + "https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46", + "https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b", + "https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c", + "https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491", + "https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68", + "https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3", + "https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7", + "https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29", + "https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a", + "https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a", + "https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8", + "https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693", + "https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31", + "https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5", + "https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8", + "https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb", + "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", + "https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47", + "https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68", + "https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3", + "https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73", + "https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19", + "https://deno.land/std@0.224.0/dotenv/load.ts": "587b342f0f6a3df071331fe6ba1c823729ab68f7d53805809475e486dd4161d7", + "https://deno.land/std@0.224.0/dotenv/mod.ts": "0180eaeedaaf88647318811cdaa418cc64dc51fb08354f91f5f480d0a1309f7d", + "https://deno.land/std@0.224.0/dotenv/parse.ts": "09977ff88dfd1f24f9973a338f0f91bbdb9307eb5ff6085446e7c423e4c7ba0c", + "https://deno.land/std@0.224.0/dotenv/stringify.ts": "275da322c409170160440836342eaa7cf012a1d11a7e700d8ca4e7f2f8aa4615", + "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5", + "https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6", + "https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2", + "https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e", + "https://deno.land/x/zod@v3.22.4/ZodError.ts": "4de18ff525e75a0315f2c12066b77b5c2ae18c7c15ef7df7e165d63536fdf2ea", + "https://deno.land/x/zod@v3.22.4/errors.ts": "5285922d2be9700cc0c70c95e4858952b07ae193aa0224be3cbd5cd5567eabef", + "https://deno.land/x/zod@v3.22.4/external.ts": "a6cfbd61e9e097d5f42f8a7ed6f92f93f51ff927d29c9fbaec04f03cbce130fe", + "https://deno.land/x/zod@v3.22.4/helpers/enumUtil.ts": "54efc393cc9860e687d8b81ff52e980def00fa67377ad0bf8b3104f8a5bf698c", + "https://deno.land/x/zod@v3.22.4/helpers/errorUtil.ts": "7a77328240be7b847af6de9189963bd9f79cab32bbc61502a9db4fe6683e2ea7", + "https://deno.land/x/zod@v3.22.4/helpers/parseUtil.ts": "f791e6e65a0340d85ad37d26cd7a3ba67126cd9957eac2b7163162155283abb1", + "https://deno.land/x/zod@v3.22.4/helpers/partialUtil.ts": "998c2fe79795257d4d1cf10361e74492f3b7d852f61057c7c08ac0a46488b7e7", + "https://deno.land/x/zod@v3.22.4/helpers/typeAliases.ts": "0fda31a063c6736fc3cf9090dd94865c811dfff4f3cb8707b932bf937c6f2c3e", + "https://deno.land/x/zod@v3.22.4/helpers/util.ts": "8baf19b19b2fca8424380367b90364b32503b6b71780269a6e3e67700bb02774", + "https://deno.land/x/zod@v3.22.4/index.ts": "d27aabd973613985574bc31f39e45cb5d856aa122ef094a9f38a463b8ef1a268", + "https://deno.land/x/zod@v3.22.4/locales/en.ts": "a7a25cd23563ccb5e0eed214d9b31846305ddbcdb9c5c8f508b108943366ab4c", + "https://deno.land/x/zod@v3.22.4/mod.ts": "64e55237cb4410e17d968cd08975566059f27638ebb0b86048031b987ba251c4", + "https://deno.land/x/zod@v3.22.4/types.ts": "724185522fafe43ee56a52333958764c8c8cd6ad4effa27b42651df873fc151e" + } +} diff --git a/backend/generate-schemas.sh b/backend/generate-schemas.sh new file mode 100755 index 0000000..f3f3c07 --- /dev/null +++ b/backend/generate-schemas.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +generate_schema_for_type() { + yes | npx typescript-json-schema --noExtraProps true --required --refs false --out "temp_schemas/$1.json" "./types/**.ts" "$1" +} + +generate_schemas() { + rm -rf temp_schemas + mkdir temp_schemas + + generate_schema_for_type "AiSuggestionsOutput" & + generate_schema_for_type "AiSnewuggestionOutput" & + wait + + rm -rf ./schemas + mv temp_schemas schemas +} + +# Run the schema generation +generate_schemas +echo "Schema generated successfully!" diff --git a/backend/main.ts b/backend/main.ts new file mode 100644 index 0000000..be3f7dc --- /dev/null +++ b/backend/main.ts @@ -0,0 +1,190 @@ +const PORT = parseInt(Deno.env.get("PORT") || "8000"); +import { + GetNewSuggestInput, + GetNewSuggestOutput, + GetSongsTextParams, + GetTrackInput, + GetYoutubeInput, +} from "./types/api.ts"; +import { getSong } from "./routes/get-song.ts"; +import { getSpotifyTrackById } from "./routes/get-track.ts"; +import { getVids } from "./routes/get-video.ts"; +import { getSpotifyAccessToken } from "./spotify-api.ts"; +import { generateVibes } from "./ai-suggestion.ts"; +import { getNewSuggestion } from "./routes/get-new-suggest.ts"; + +const routes = [ + { + pattern: new URLPattern({ pathname: "/music" }), + handler: async (req: Request) => { + const params = await req.json(); + const songs = await getSong(params); + + return new Response(JSON.stringify(songs), { + status: 200, + }); + }, + }, + { + pattern: new URLPattern({ pathname: "/health" }), + handler: (_req: Request) => { + return new Response("All good", { + status: 200, + }); + }, + }, + { + pattern: new URLPattern({ pathname: "/video" }), + handler: async (req: Request) => { + const params: GetYoutubeInput = await req.json(); + const vids = await getVids(params); + + return new Response(JSON.stringify(vids), { + status: 200, + }); + }, + }, + { + pattern: new URLPattern({ pathname: "/track" }), + handler: async (req: Request) => { + const params: GetTrackInput = await req.json(); + const track = await getSpotifyTrackById(params.trackId); + + return new Response(JSON.stringify(track), { + status: 200, + }); + }, + }, + { + pattern: new URLPattern({ pathname: "/newSuggestions" }), + handler: async (req: Request) => { + const params: GetNewSuggestInput = await req.json(); + const suggestions: GetNewSuggestOutput = await getNewSuggestion(params); + return new Response(JSON.stringify(suggestions), { + status: 200, + }); + }, + }, +]; + +const cors = { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "POST, GET, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type, Authorization", +}; + +const handler = async (req: Request): Promise => { + if (req.method === "OPTIONS") { + return new Response(null, { + status: 204, + headers: cors, + }); + } + + let response: Response | undefined; + try { + for (const route of routes) { + const match = route.pattern.exec(req.url); + if (match) { + response = await route.handler(req); + break; + } + } + } catch (error: any) { + console.error("Server error:", error); + response = new Response(error.toString() || "Internal server error", { + status: 500, + }); + } + + response ??= new Response("Not found", { status: 404 }); + + for (const [key, value] of Object.entries(cors)) { + response.headers.set(key, value); + } + return response; +}; + +Deno.serve({ hostname: "0.0.0.0", port: PORT }, handler); + +Deno.test("music API complete workflow", async () => { + const spotify_api_key = getSpotifyAccessToken(); + if (!spotify_api_key) throw Error("Testing token not found"); + + const testInput: GetSongsTextParams = { + prompt: "i want summer feeling music", + rules: "make it fun and suitable for children", + }; + const res = await fetch(`http://localhost:${PORT}/music`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(testInput), + }); + + if (res.status !== 200) throw new Error(`Unexpected status: ${res.status}`); + const json = await res.json(); + console.log("API Response:", json); +}); + +Deno.test("video API complete workflow", async () => { + const yt_api_key = Deno.env.get("YT_API_KEY"); + if (!yt_api_key) throw Error("Testing token not found"); + + const input: GetYoutubeInput = { + title: "Here Comes The Sun", + artist: "The Beatles", + }; + const res = await fetch(`http://localhost:${PORT}/video`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(input), + }); + + if (res.status !== 200) throw new Error(`Unexpected status: ${res.status}`); + const json = await res.json(); + console.log("Video API Response:", json); +}); + +Deno.test("track API complete workflow", async () => { + const spotify_api_key = getSpotifyAccessToken(); + if (!spotify_api_key) throw Error("Testing token not found"); + + const input: GetTrackInput = { trackId: "6dGnYIeXmHdcikdzNNDMm2" }; + const res = await fetch(`http://localhost:${PORT}/track`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(input), + }); + + if (res.status !== 200) throw new Error(`Unexpected status: ${res.status}`); + const json = await res.json(); + console.log("Track API Response:", json); +}); + +Deno.test("new suggestion API complete workflow", async () => { + const testInput: GetNewSuggestInput = { + type: "from-songs-suggestion", + rules: "The songs should be upbeat and suitable for a party", + songs: [ + { song: "Thriller - Michael Jackson" }, + { song: "Uptown Funk - Bruno Mars" }, + { song: "24K Magic - Bruno Mars" }, + ], + }; + + const res = await fetch(`http://localhost:${PORT}/newSuggestions`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(testInput), + }); + + if (res.status !== 200) throw new Error(`Unexpected status: ${res.status}`); + + const json = await res.json(); + console.log("New Suggestion API Response:", json); + + // Validate that the response contains a songs array. + if (!json.songs || !Array.isArray(json.songs)) { + throw new Error("Response does not contain a valid songs array"); + } +}); diff --git a/backend/routes/get-new-suggest.ts b/backend/routes/get-new-suggest.ts new file mode 100644 index 0000000..b5d2733 --- /dev/null +++ b/backend/routes/get-new-suggest.ts @@ -0,0 +1,15 @@ +import { searchSpotifyTracks } from "../spotify-api.ts"; +import { GetNewSuggestInput, GetNewSuggestOutput } from "../types/api.ts"; +import { generateVibes } from "../ai-suggestion.ts"; + +export async function getNewSuggestion( + params: GetNewSuggestInput, +): Promise { + const ai_suggestion = await generateVibes(params); + + const suggestionResults = await searchSpotifyTracks( + ai_suggestion.songs, + ); + + return { songs: suggestionResults }; +} diff --git a/backend/routes/get-song.ts b/backend/routes/get-song.ts new file mode 100644 index 0000000..c96a114 --- /dev/null +++ b/backend/routes/get-song.ts @@ -0,0 +1,18 @@ +import { generateSongs } from "../ai-selector.ts"; +import { searchSpotifyTracks } from "../spotify-api.ts"; +import { GetSongsTextOutput, GetSongsTextParams } from "../types/api.ts"; + +export async function getSong( + params: GetSongsTextParams, +): Promise { + const ai_response = await generateSongs(params.prompt, params.rules); + if (ai_response.type == "error") { + return ai_response; + } + + const spotifyResults = await searchSpotifyTracks( + ai_response.songs, + ); + + return { type: "success", songs: spotifyResults }; +} diff --git a/backend/routes/get-track.ts b/backend/routes/get-track.ts new file mode 100644 index 0000000..4e6d380 --- /dev/null +++ b/backend/routes/get-track.ts @@ -0,0 +1,87 @@ +import { SpotifyTrack } from "../types/spotify.ts"; +import { getSpotifyAccessToken } from "../spotify-api.ts"; +import { assert } from "jsr:@std/assert/assert"; + +// Simple in-memory cache for track objects by ID +const trackCache = new Map(); + +/** + * Inner function that performs the actual API call without retries. + */ +async function fetchSpotifyTrackById(trackId: string): Promise { + const accessToken = await getSpotifyAccessToken(); + const url = `https://api.spotify.com/v1/tracks/${trackId}?market=US`; + + const response = await fetch(url, { + headers: { + "Authorization": `Bearer ${accessToken}`, + }, + }); + + if (!response.ok) { + throw new Error( + `Failed to fetch track: ${response.status} ${await response.text()}`, + ); + } + + const track: SpotifyTrack = await response.json(); + + // Cache the result + trackCache.set(trackId, track); + + return track; +} + +/** + * Fetches a Spotify track by its ID, with caching. + * Returns the full SpotifyTrack object. + */ +export async function getSpotifyTrackById( + trackId: string, +): Promise { + // Check cache first + if (trackCache.has(trackId)) { + return trackCache.get(trackId)!; + } + + const maxRetries = 10; + let lastError: Error; + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + return await fetchSpotifyTrackById(trackId); + } catch (error) { + lastError = error as Error; + if (attempt === maxRetries) { + throw Error(`Attempt ${attempt}: ${lastError}`); + } + // Optional: add delay between retries + await new Promise((resolve) => setTimeout(resolve, 1000 * attempt)); + } + } + + throw lastError!; +} + +Deno.test("getSpotifyTrackById basic", async () => { + // Example: Blinding Lights by The Weeknd + const trackId = "2EqlS6tkEnglzr7tkKAAYD"; + + // First call (should fetch from API) + const track1 = await getSpotifyTrackById(trackId); + assert(track1.id === trackId); + assert(typeof track1.name === "string"); + assert(Array.isArray(track1.artists)); + + // Second call (should hit cache) + const start = performance.now(); + const track2 = await getSpotifyTrackById(trackId); + const elapsed = performance.now() - start; + + console.log(track2); + console.log("Second call elapsed:", elapsed, "ms"); + assert(track2.id === trackId); + assert(typeof track2.name === "string"); + assert(Array.isArray(track2.artists)); + assert(elapsed < 10, `Second call took too long: ${elapsed}ms`); +}); diff --git a/backend/routes/get-video.ts b/backend/routes/get-video.ts new file mode 100644 index 0000000..d9dfbd1 --- /dev/null +++ b/backend/routes/get-video.ts @@ -0,0 +1,9 @@ +import { GetYoutubeInput, GetYoutubeOutput } from "../types/api.ts"; +import { searchYTVideo } from "../youtube-api.ts"; + +export async function getVids( + params: GetYoutubeInput, +): Promise { + const videoId = await searchYTVideo(params); + return { videoId }; +} diff --git a/backend/schemas/AiSnewuggestionOutput.json b/backend/schemas/AiSnewuggestionOutput.json new file mode 100644 index 0000000..fc10502 --- /dev/null +++ b/backend/schemas/AiSnewuggestionOutput.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "songs": { + "items": { + "additionalProperties": false, + "properties": { + "artist": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "artist", + "title" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "songs" + ], + "type": "object" +} + diff --git a/backend/schemas/AiSuggestionsOutput.json b/backend/schemas/AiSuggestionsOutput.json new file mode 100644 index 0000000..4228a20 --- /dev/null +++ b/backend/schemas/AiSuggestionsOutput.json @@ -0,0 +1,56 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "songs": { + "items": { + "additionalProperties": false, + "properties": { + "artist": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "artist", + "title" + ], + "type": "object" + }, + "type": "array" + }, + "type": { + "const": "success", + "type": "string" + } + }, + "required": [ + "songs", + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "error": { + "type": "string" + }, + "type": { + "const": "error", + "type": "string" + } + }, + "required": [ + "error", + "type" + ], + "type": "object" + } + ] +} + diff --git a/backend/spotify-api.ts b/backend/spotify-api.ts new file mode 100644 index 0000000..483c59d --- /dev/null +++ b/backend/spotify-api.ts @@ -0,0 +1,136 @@ +import { assert } from "jsr:@std/assert/assert"; +import { SpotifySearchResponse, SpotifyTrack } from "./types/spotify.ts"; +import { SongSearch } from "./types/ai.ts"; + +export async function searchSpotifyTracks( + songs: SongSearch[], +): Promise { + const results: SpotifyTrack[] = []; + + const trackPromises = songs.map(async (song) => { + try { + const query = `track:"${song.title}" artist:"${song.artist}"`; + + const params = new URLSearchParams({ + q: query, + type: "track", + market: "US", + limit: "1", + offset: "0", + }); + + const url = `https://api.spotify.com/v1/search?${params.toString()}`; + + const response = await fetch(url, { + headers: { + "Authorization": "Bearer " + await getSpotifyAccessToken(), + }, + }); + + if (!response.ok) { + throw Error("Unsuccesful response"); + } + + const data: SpotifySearchResponse = await response.json(); + + const track = data.tracks.items.at(0); + if (track) { + return track; + } + } catch (error) { + console.error(error); + } + return undefined; + }); + + const tracks = await Promise.all(trackPromises); + for (const track of tracks) { + if (track) { + results.push(track); + } + } + + return results; +} + +/** + * Get Spotify access token using Client Credentials Flow. + * Reads credentials from environment variables, caches token in memory with expiry. + * Throws if credentials are missing or request fails. + */ +let spotifyTokenCache: { token: string; expiresAt: number } | null = null; + +export async function getSpotifyAccessToken(): Promise { + const clientId = Deno.env.get("SPOTIFY_CLIENT_ID"); + const clientSecret = Deno.env.get("SPOTIFY_CLIENT_SECRET"); + + if (!clientId || !clientSecret) { + throw new Error("Spotify credentials not found in environment variables."); + } + + // If cached and not expired, return cached token + if ( + spotifyTokenCache && + spotifyTokenCache.expiresAt > Date.now() + ) { + return spotifyTokenCache.token; + } + + const tokenResponse = await fetch("https://accounts.spotify.com/api/token", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + "Authorization": "Basic " + + btoa(`${clientId}:${clientSecret}`), + }, + body: "grant_type=client_credentials", + }); + + if (!tokenResponse.ok) { + throw new Error( + `Failed to fetch Spotify access token: ${tokenResponse.status} ${await tokenResponse + .text()}`, + ); + } + + const tokenData = await tokenResponse.json(); + const accessToken = tokenData.access_token; + const expiresIn = tokenData.expires_in; // seconds + + if (!accessToken || !expiresIn) { + throw new Error("Invalid token response from Spotify."); + } + + // Cache token with expiry + spotifyTokenCache = { + token: accessToken, + expiresAt: Date.now() + expiresIn * 1000 - 10_000, // 10s early + }; + + return accessToken; +} + +Deno.test("getSpotifyAccessToken caching", async () => { + await getSpotifyAccessToken(); + + const start2 = performance.now(); + const token2 = await getSpotifyAccessToken(); + const elapsed2 = performance.now() - start2; + + assert(elapsed2 < 10, `Second call took too long: ${elapsed2}ms`); +}); + +Deno.test("searchSpotifyTracks basic", async () => { + const songs = [ + { title: "Blinding Lights", artist: "The Weeknd" }, + { title: "Levitating", artist: "Dua Lipa" }, + ]; + + const spotify_api_key = getSpotifyAccessToken(); + if (!spotify_api_key) throw Error("Testing token not found"); + const tracks = await searchSpotifyTracks(songs); + + console.log(tracks); + + assert(tracks.length > 0); +}); diff --git a/backend/types/ai.ts b/backend/types/ai.ts new file mode 100644 index 0000000..517c169 --- /dev/null +++ b/backend/types/ai.ts @@ -0,0 +1,19 @@ +export type AiSuggestionsOutput = { + type: "success"; + songs: { + title: string; + artist: string; + }[]; +} | { + type: "error"; + error: string; +}; + +export interface SongSearch { + title: string; + artist: string; +} + +export interface AiSnewuggestionOutput { + songs: SongSearch[]; +} diff --git a/backend/types/api.ts b/backend/types/api.ts new file mode 100644 index 0000000..1b7f82e --- /dev/null +++ b/backend/types/api.ts @@ -0,0 +1,47 @@ +import { SpotifyTrack } from "./spotify.ts"; + +export interface GetSongsTextParams { + prompt: string; + rules: string; +} + +export type GetSongsTextOutput = { + type: "success"; + songs: SpotifyTrack[]; +} | { + type: "error"; + error: string; +}; + +export interface GetYoutubeInput { + title: string; + artist: string; +} + +export interface GetYoutubeOutput { + videoId?: string; +} + +export interface GetTrackInput { + trackId: string; +} + +export interface GetTrackOutput { + track: SpotifyTrack; +} + +export type GetNewSuggestInput = + & { + rules: string; + } + & ({ + type: "scratch-suggestion"; + room_name: string; + } | { + type: "from-songs-suggestion"; + songs: { song: string }[]; + }); + +export type GetNewSuggestOutput = { + songs: SpotifyTrack[]; +}; diff --git a/backend/types/spotify.ts b/backend/types/spotify.ts new file mode 100644 index 0000000..e35b553 --- /dev/null +++ b/backend/types/spotify.ts @@ -0,0 +1,97 @@ +export interface SpotifyArtist { + external_urls: { + spotify: string; + }; + href: string; + id: string; + name: string; + type: string; + uri: string; +} + +export interface SpotifyAlbum { + album_type: string; + total_tracks: number; + available_markets: string[]; + external_urls: { + spotify: string; + }; + href: string; + id: string; + images: Array<{ + url: string; + height: number; + width: number; + }>; + name: string; + release_date: string; + release_date_precision: string; + type: string; + uri: string; + artists: SpotifyArtist[]; + is_playable?: boolean; +} + +export interface SpotifyTrack { + album: SpotifyAlbum; + artists: SpotifyArtist[]; + available_markets: string[]; + disc_number: number; + duration_ms: number; + explicit: boolean; + external_ids: { + isrc?: string; + }; + external_urls: { + spotify: string; + }; + href: string; + id: string; + is_playable?: boolean; + name: string; + popularity: number; + preview_url: string | null; + track_number: number; + type: string; + uri: string; + is_local: boolean; +} + +export interface SpotifySearchResponse { + tracks: { + href: string; + limit: number; + next: string | null; + offset: number; + previous: string | null; + total: number; + items: SpotifyTrack[]; + }; +} + +export interface SearchResultTrack { + id: string; + name: string; + artists: Array<{ + id: string; + name: string; + spotify_url: string; + }>; + album: { + id: string; + name: string; + release_date: string; + images: Array<{ + url: string; + height: number; + width: number; + }>; + spotify_url: string; + }; + duration_ms: number; + popularity: number; + preview_url: string | null; + spotify_url: string; + explicit: boolean; + available_markets: string[]; +} diff --git a/backend/types/youtube.ts b/backend/types/youtube.ts new file mode 100644 index 0000000..2ae6745 --- /dev/null +++ b/backend/types/youtube.ts @@ -0,0 +1,4 @@ +export interface Vids { + title: string; + artist: string; +} diff --git a/backend/youtube-api.ts b/backend/youtube-api.ts new file mode 100644 index 0000000..0c5a6f6 --- /dev/null +++ b/backend/youtube-api.ts @@ -0,0 +1,46 @@ +import { assert } from "jsr:@std/assert/assert"; +import { Vids } from "./types/youtube.ts"; + +export async function searchYTVideo( + videos: Vids, +): Promise { + const query = `${videos.title} ${videos.artist}`; + const params = new URLSearchParams({ + part: "snippet", + q: query, + type: "video", + key: Deno.env.get("YT_API_KEY")!, // Replace with your actual API key + }); + + const endpoint = + `https://www.googleapis.com/youtube/v3/search?${params.toString()}`; + + const response = await fetch(endpoint); + if (!response.ok) { + throw new Error(`YouTube API error: ${response.statusText}`); + } + const data = await response.json(); + + const videoId = data.items && data.items.length > 0 + ? data.items[0].id.videoId + : undefined; + + if (!videoId) { + throw new Error("No video found for the given title and artist."); + } + + return videoId; +} + +Deno.test("searchYT", async () => { + const yt_api_key = Deno.env.get("YT_API_KEY"); + if (!yt_api_key) throw Error("Testing token not found"); + const videoId = await searchYTVideo({ + title: "Here Comes The Sun", + artist: "The Beatles", + }); + + console.log("Here's the video ID:", videoId); + + assert(typeof videoId === "string" && videoId); +}); diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..3b462cb --- /dev/null +++ b/frontend/.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/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 0000000..7d74fe2 --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,9 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock +bun.lock +bun.lockb + +# Miscellaneous +/static/ diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..b946774 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,17 @@ +{ + "useTabs": false, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "tabWidth": 4, + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ], + "tailwindStylesheet": "./src/app.css" +} diff --git a/frontend/.zed/tasks.json b/frontend/.zed/tasks.json new file mode 100644 index 0000000..ca2d38b --- /dev/null +++ b/frontend/.zed/tasks.json @@ -0,0 +1,6 @@ +[ + { + "label": "Generate types", + "command": "yes | npx supabase gen types typescript --project-id htdssaqidnitlbswknqw > src/lib/database.types.ts" + } +] diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..da85e4e --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,21 @@ +# Use the Node alpine official image +# https://hub.docker.com/_/node +FROM node:lts-alpine + +# Create and change to the app directory. +WORKDIR /app + +# Copy the files to the container image +COPY frontend/package*.json ./ + +# Install packages +RUN npm ci + +# Copy local code to the container image. +COPY frontend/ ./ + +# Build the app. +RUN npm run build + +# Serve the app +CMD ["npm", "run", "start"] diff --git a/frontend/components.json b/frontend/components.json new file mode 100644 index 0000000..a4058ef --- /dev/null +++ b/frontend/components.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://shadcn-svelte.com/schema.json", + "tailwind": { + "css": "src/app.css", + "baseColor": "zinc" + }, + "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/frontend/cspell.json b/frontend/cspell.json new file mode 100644 index 0000000..c049d03 --- /dev/null +++ b/frontend/cspell.json @@ -0,0 +1,6 @@ +{ + "words": [ + "supabase", + "interted" + ] +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..cface62 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,4740 @@ +{ + "name": "frontend", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.1", + "dependencies": { + "@supabase/supabase-js": "^2.53.0", + "@types/qrcode": "^1.5.5", + "qrcode": "^1.5.4" + }, + "devDependencies": { + "@internationalized/date": "^3.8.2", + "@lucide/svelte": "^0.515.0", + "@sveltejs/adapter-auto": "^6.0.1", + "@sveltejs/adapter-node": "^5.2.13", + "@sveltejs/kit": "^2.26.1", + "@sveltejs/vite-plugin-svelte": "^6.1.0", + "@tailwindcss/vite": "^4.1.11", + "@tanstack/table-core": "^8.21.3", + "bits-ui": "^2.9.0", + "clsx": "^2.1.1", + "embla-carousel-svelte": "^8.6.0", + "formsnap": "^2.0.1", + "layerchart": "^2.0.0-next.27", + "mode-watcher": "^1.1.0", + "paneforge": "^1.0.1", + "prettier": "^3.6.2", + "prettier-plugin-svelte": "^3.4.0", + "prettier-plugin-tailwindcss": "^0.6.14", + "svelte": "^5.37.1", + "svelte-check": "^4.3.0", + "svelte-sonner": "^1.0.5", + "sveltekit-superforms": "^2.27.1", + "tailwind-merge": "^3.3.1", + "tailwind-variants": "^1.0.0", + "tailwindcss": "^4.1.11", + "tw-animate-css": "^1.3.6", + "typescript": "^5.9.2", + "vaul-svelte": "^1.0.0-next.7", + "vite": "^7.0.6" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ark/schema": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@ark/schema/-/schema-0.46.0.tgz", + "integrity": "sha512-c2UQdKgP2eqqDArfBqQIJppxJHvNNXuQPeuSPlDML4rjw+f1cu0qAlzOG4b8ujgm9ctIDWwhpyw6gjG5ledIVQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@ark/util": "0.46.0" + } + }, + "node_modules/@ark/util": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@ark/util/-/util-0.46.0.tgz", + "integrity": "sha512-JPy/NGWn/lvf1WmGCPw2VGpBg5utZraE84I7wli18EDF3p3zc/e9WolT35tINeZO3l7C77SjqRJeAUoT0CvMRg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@babel/runtime": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@dagrejs/dagre": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.1.5.tgz", + "integrity": "sha512-Ghgrh08s12DCL5SeiR6AoyE80mQELTWhJBRmXfFoqDiFkR458vPEdgTbbjA0T+9ETNxUblnD0QW55tfdvi5pjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dagrejs/graphlib": "2.2.4" + } + }, + "node_modules/@dagrejs/graphlib": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.2.4.tgz", + "integrity": "sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">17.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@exodus/schemasafe": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz", + "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@gcornut/valibot-json-schema": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@gcornut/valibot-json-schema/-/valibot-json-schema-0.42.0.tgz", + "integrity": "sha512-4Et4AN6wmqeA0PfU5Clkv/IS27wiefsWf6TemAZrb75uzkClYEFavim7SboeKwbll9Nbsn2Iv0LT/HS5H7orZg==", + "dev": true, + "optional": true, + "dependencies": { + "valibot": "~0.42.0" + }, + "bin": { + "valibot-json-schema": "bin/index.js" + }, + "optionalDependencies": { + "@types/json-schema": ">= 7.0.14", + "esbuild-runner": ">= 2.2.2" + } + }, + "node_modules/@gcornut/valibot-json-schema/node_modules/valibot": { + "version": "0.42.1", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.42.1.tgz", + "integrity": "sha512-3keXV29Ar5b//Hqi4MbSdV7lfVp6zuYLZuA9V1PvQUsXqogr+u5lvLPLk3A4f74VUXDnf/JfWMN6sB+koJ/FFw==", + "dev": true, + "license": "MIT", + "optional": true, + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@internationalized/date": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.2.tgz", + "integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@layerstack/svelte-actions": { + "version": "1.0.1-next.12", + "resolved": "https://registry.npmjs.org/@layerstack/svelte-actions/-/svelte-actions-1.0.1-next.12.tgz", + "integrity": "sha512-dndWTlYu8b1u6vw2nrO7NssccoACArGG75WoNlyVC13KuENZlWdKE9Q79/wlnbq00NeQMNKMjJwRMsrKQj2ULA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.0", + "@layerstack/utils": "2.0.0-next.12", + "d3-scale": "^4.0.2" + } + }, + "node_modules/@layerstack/svelte-state": { + "version": "0.1.0-next.17", + "resolved": "https://registry.npmjs.org/@layerstack/svelte-state/-/svelte-state-0.1.0-next.17.tgz", + "integrity": "sha512-z7e6mPJnypD80LEI/UDuH0bI6s8/nut06MB7rEkRcEfHJekhKSJgFhMnrYzLED7Mc2gTTD0X/wcYlakauWlU8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@layerstack/utils": "2.0.0-next.12" + } + }, + "node_modules/@layerstack/tailwind": { + "version": "2.0.0-next.15", + "resolved": "https://registry.npmjs.org/@layerstack/tailwind/-/tailwind-2.0.0-next.15.tgz", + "integrity": "sha512-7tqKE3OV7/ybeDOORX++USYYCBJa7IgTya2czFpzbgXGo7CQDVyuv+0J1DggjRcEqhhXQA4MUhgnhcRaZvHxWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@layerstack/utils": "^2.0.0-next.12", + "clsx": "^2.1.1", + "d3-array": "^3.2.4", + "lodash-es": "^4.17.21", + "tailwind-merge": "^3.2.0" + } + }, + "node_modules/@layerstack/utils": { + "version": "2.0.0-next.12", + "resolved": "https://registry.npmjs.org/@layerstack/utils/-/utils-2.0.0-next.12.tgz", + "integrity": "sha512-fhGZUlSr3N+D44BYm37WKMGSEFyZBW+dwIqtGU8Cl54mR4TLQ/UwyGhdpgIHyH/x/8q1abE0fP0Dn6ZsrDE3BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "d3-array": "^3.2.4", + "d3-time": "^3.1.0", + "d3-time-format": "^4.1.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/@lucide/svelte": { + "version": "0.515.0", + "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-0.515.0.tgz", + "integrity": "sha512-CEAyqcZmNBfYzVgaRmK2RFJP5tnbXxekRyDk0XX/eZQRfsJmkDvmQwXNX8C869BgNeryzmrRyjHhUL6g9ZOHNA==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "svelte": "^5" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@poppinss/macroable": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@poppinss/macroable/-/macroable-1.0.5.tgz", + "integrity": "sha512-6u61y1HHd090MEk1Av0/1btDmm2Hh/+XoJj+HgFYRh9koUPI822ybJbwLHuqjLNCiY+o1gRykg2igEqOf/VBZw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "28.0.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz", + "integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", + "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.38", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz", + "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@supabase/auth-js": { + "version": "2.71.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.71.1.tgz", + "integrity": "sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.5.tgz", + "integrity": "sha512-v5GSqb9zbosquTo6gBwIiq7W9eQ7rE5QazsK/ezNiQXdCbY+bH8D9qEaBIkhVvX4ZRW5rP03gEfw5yw9tiq4EQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz", + "integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.11.15", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.15.tgz", + "integrity": "sha512-HQKRnwAqdVqJW/P9TjKVK+/ETpW4yQ8tyDPPtRMKOH4Uh3vQD74vmj353CYs8+YwVBKubeUOOEpI9CT8mT4obw==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.13", + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "isows": "^1.0.7", + "ws": "^8.18.2" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.10.4.tgz", + "integrity": "sha512-cvL02GarJVFcNoWe36VBybQqTVRq6wQSOCvTS64C+eyuxOruFIm1utZAY0xi2qKtHJO3EjKaj8iWJKySusDmAQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.53.0", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.53.0.tgz", + "integrity": "sha512-Vg9sl0oFn55cCPaEOsDsRDbxOVccxRrK/cikjL1XbywHEOfyA5SOOEypidMvQLwgoAfnC2S4D9BQwJDcZs7/TQ==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.71.1", + "@supabase/functions-js": "2.4.5", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.19.4", + "@supabase/realtime-js": "2.11.15", + "@supabase/storage-js": "^2.10.4" + } + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz", + "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-auto": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-6.0.1.tgz", + "integrity": "sha512-mcWud3pYGPWM2Pphdj8G9Qiq24nZ8L4LB7coCUckUEy5Y7wOWGJ/enaZ4AtJTcSm5dNK1rIkBRoqt+ae4zlxcQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@sveltejs/kit": "^2.0.0" + } + }, + "node_modules/@sveltejs/adapter-node": { + "version": "5.2.13", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.13.tgz", + "integrity": "sha512-yS2TVFmIrxjGhYaV5/iIUrJ3mJl6zjaYn0lBD70vTLnYvJeqf3cjvLXeXCUCuYinhSBoyF4DpfGla49BnIy7sQ==", + "dev": true, + "license": "MIT", + "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" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.26.1.tgz", + "integrity": "sha512-FwDhHAoXYUGnYndrrEzEYcKdYWpSoRKq4kli29oMe83hLri4/DOGQk3xUgwjDo0LKpSmj5M/Sj29/Ug3wO0Cbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", + "cookie": "^0.6.0", + "devalue": "^5.1.0", + "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" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@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" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.1.0.tgz", + "integrity": "sha512-+U6lz1wvGEG/BvQyL4z/flyNdQ9xDNv5vrh+vWBWTHaebqT0c9RNggpZTo/XSPoHsSCWBlYaTlRX8pZ9GATXCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0-next.1", + "debug": "^4.4.1", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.17", + "vitefu": "^1.1.1" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite": "^6.3.0 || ^7.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.0.tgz", + "integrity": "sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.1" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", + "svelte": "^5.0.0", + "vite": "^6.3.0 || ^7.0.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-x64": "4.1.11", + "@tailwindcss/oxide-freebsd-x64": "4.1.11", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.11", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", + "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", + "tailwindcss": "4.1.11" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/node": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", + "license": "MIT" + }, + "node_modules/@types/qrcode": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz", + "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.15.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.2.tgz", + "integrity": "sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typeschema/class-validator": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@typeschema/class-validator/-/class-validator-0.3.0.tgz", + "integrity": "sha512-OJSFeZDIQ8EK1HTljKLT5CItM2wsbgczLN8tMEfz3I1Lmhc5TBfkZ0eikFzUC16tI3d1Nag7um6TfCgp2I2Bww==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@typeschema/core": "0.14.0" + }, + "peerDependencies": { + "class-validator": "^0.14.1" + }, + "peerDependenciesMeta": { + "class-validator": { + "optional": true + } + } + }, + "node_modules/@typeschema/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@typeschema/core/-/core-0.14.0.tgz", + "integrity": "sha512-Ia6PtZHcL3KqsAWXjMi5xIyZ7XMH4aSnOQes8mfMLx+wGFGtGRNlwe6Y7cYvX+WfNK67OL0/HSe9t8QDygV0/w==", + "dev": true, + "license": "MIT", + "optional": true, + "peerDependencies": { + "@types/json-schema": "^7.0.15" + }, + "peerDependenciesMeta": { + "@types/json-schema": { + "optional": true + } + } + }, + "node_modules/@vinejs/compiler": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vinejs/compiler/-/compiler-3.0.0.tgz", + "integrity": "sha512-v9Lsv59nR56+bmy2p0+czjZxsLHwaibJ+SV5iK9JJfehlJMa501jUJQqqz4X/OqKXrxtE3uTQmSqjUqzF3B2mw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@vinejs/vine": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@vinejs/vine/-/vine-3.0.1.tgz", + "integrity": "sha512-ZtvYkYpZOYdvbws3uaOAvTFuvFXoQGAtmzeiXu+XSMGxi5GVsODpoI9Xu9TplEMuD/5fmAtBbKb9cQHkWkLXDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@poppinss/macroable": "^1.0.4", + "@types/validator": "^13.12.2", + "@vinejs/compiler": "^3.0.0", + "camelcase": "^8.0.0", + "dayjs": "^1.11.13", + "dlv": "^1.1.3", + "normalize-url": "^8.0.1", + "validator": "^13.12.0" + }, + "engines": { + "node": ">=18.16.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arktype": { + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/arktype/-/arktype-2.1.20.tgz", + "integrity": "sha512-IZCEEXaJ8g+Ijd59WtSYwtjnqXiwM8sWQ5EjGamcto7+HVN9eK0C4p0zDlCuAwWhpqr6fIBkxPuYDl4/Mcj/+Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@ark/schema": "0.46.0", + "@ark/util": "0.46.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bits-ui": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.9.0.tgz", + "integrity": "sha512-5lj7L4/TvC0SQHfynwGSXFXO9mDeVszW4+C+lDbEMQFesWT78Z3tkfPTzGYsm8PBauP847uE5xovDGAhndjuog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.1", + "@floating-ui/dom": "^1.7.1", + "esm-env": "^1.1.2", + "runed": "^0.29.1", + "svelte-toolbelt": "^0.9.3", + "tabbable": "^6.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/huntabyte" + }, + "peerDependencies": { + "@internationalized/date": "^3.8.1", + "svelte": "^5.33.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/class-validator": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz", + "integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.11.1", + "validator": "^13.9.0" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dev": true, + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo-voronoi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/d3-geo-voronoi/-/d3-geo-voronoi-2.1.0.tgz", + "integrity": "sha512-kqE4yYuOjPbKdBXG0xztCacPwkVSK2REF1opSNrnqqtXJmNcM++UbwQ8SxvwP6IQTj9RvIjjK4qeiVsEfj0Z2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-delaunay": "6", + "d3-geo": "3", + "d3-tricontour": "1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate-path": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-interpolate-path/-/d3-interpolate-path-2.3.0.tgz", + "integrity": "sha512-tZYtGXxBmbgHsIc9Wms6LS5u4w6KbP8C09a4/ZYc4KLMYYqub57rRBUgpUr2CIarIrJEpdAWWxWQvofgaMpbKQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "dev": true, + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-tile": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d3-tile/-/d3-tile-1.0.0.tgz", + "integrity": "sha512-79fnTKpPMPDS5xQ0xuS9ir0165NEwwkFpe/DSOmc2Gl9ldYzKKRDWogmTTE8wAJ8NA7PMapNfEcyKhI9Lxdu5Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-tricontour": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d3-tricontour/-/d3-tricontour-1.0.2.tgz", + "integrity": "sha512-HIRxHzHagPtUPNabjOlfcyismJYIsc+Xlq4mlsts4e8eAcwyq9Tgk/sYdyhlBpQ0MHwVquc/8j+e29YjXnmxeA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-delaunay": "6", + "d3-scale": "4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dev": true, + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", + "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/effect": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.17.4.tgz", + "integrity": "sha512-VD+5bYdIN088c1fCESqYvOKQ22O2dUptO+Chy7SGMqthIVcYBvVlYSgUQT976XJaHOV2Fd0TaW94EYWHOQsbOw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/embla-carousel": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/embla-carousel-reactive-utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz", + "integrity": "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, + "node_modules/embla-carousel-svelte": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-svelte/-/embla-carousel-svelte-8.6.0.tgz", + "integrity": "sha512-ZDsKk8Sdv+AUTygMYcwZjfRd1DTh+JSUzxkOo8b9iKAkYjg+39mzbY/lwHsE3jXSpKxdKWS69hPSNuzlOGtR2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "embla-carousel": "8.6.0", + "embla-carousel-reactive-utils": "8.6.0" + }, + "peerDependencies": { + "svelte": "^3.49.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" + } + }, + "node_modules/esbuild-runner": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/esbuild-runner/-/esbuild-runner-2.2.2.tgz", + "integrity": "sha512-fRFVXcmYVmSmtYm2mL8RlUASt2TDkGh3uRcvHFOKNr/T58VrfVeKD9uT9nlgxk96u0LS0ehS/GY7Da/bXWKkhw==", + "dev": true, + "license": "Apache License 2.0", + "optional": true, + "dependencies": { + "source-map-support": "0.5.21", + "tslib": "2.4.0" + }, + "bin": { + "esr": "bin/esr.js" + }, + "peerDependencies": { + "esbuild": "*" + } + }, + "node_modules/esbuild-runner/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.0.tgz", + "integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/formsnap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/formsnap/-/formsnap-2.0.1.tgz", + "integrity": "sha512-iJSe4YKd/W6WhLwKDVJU9FQeaJRpEFuolhju7ZXlRpUVyDdqFdMP8AUBICgnVvQPyP41IPAlBa/v0Eo35iE6wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "svelte-toolbelt": "^0.5.0" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/huntabyte" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "sveltekit-superforms": "^2.19.0" + } + }, + "node_modules/formsnap/node_modules/svelte-toolbelt": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.5.0.tgz", + "integrity": "sha512-t3tenZcnfQoIeRuQf/jBU7bvTeT3TGkcEE+1EUr5orp0lR7NEpprflpuie3x9Dn0W9nOKqs3HwKGJeeN5Ok1sQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte" + ], + "dependencies": { + "clsx": "^2.1.1", + "style-to-object": "^1.0.8" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "peerDependencies": { + "svelte": "^5.0.0-next.126" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/layerchart": { + "version": "2.0.0-next.27", + "resolved": "https://registry.npmjs.org/layerchart/-/layerchart-2.0.0-next.27.tgz", + "integrity": "sha512-yt28xU8WzXq0AliX7eiC0JKZGQtO8M9FmHvt8sESNitSc/yC+fYeTghaO9lMRwcYCmi6D1NjbFyD9mWFeazNIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dagrejs/dagre": "^1.1.4", + "@layerstack/svelte-actions": "1.0.1-next.12", + "@layerstack/svelte-state": "0.1.0-next.17", + "@layerstack/tailwind": "2.0.0-next.15", + "@layerstack/utils": "2.0.0-next.12", + "d3-array": "^3.2.4", + "d3-color": "^3.1.0", + "d3-delaunay": "^6.0.4", + "d3-dsv": "^3.0.1", + "d3-force": "^3.0.0", + "d3-geo": "^3.1.1", + "d3-geo-voronoi": "^2.1.0", + "d3-hierarchy": "^3.1.2", + "d3-interpolate": "^3.0.1", + "d3-interpolate-path": "^2.3.0", + "d3-path": "^3.1.0", + "d3-quadtree": "^3.0.1", + "d3-random": "^3.0.1", + "d3-sankey": "^0.12.3", + "d3-scale": "^4.0.2", + "d3-scale-chromatic": "^3.1.0", + "d3-shape": "^3.2.0", + "d3-tile": "^1.0.0", + "d3-time": "^3.1.0", + "lodash-es": "^4.17.21", + "memoize": "^10.1.0", + "runed": "^0.28.0" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } + }, + "node_modules/layerchart/node_modules/runed": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.28.0.tgz", + "integrity": "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "license": "MIT", + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.12.10", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.10.tgz", + "integrity": "sha512-E91vHJD61jekHHR/RF/E83T/CMoaLXT7cwYA75T4gim4FZjnM6hbJjVIGg7chqlSqRsSvQ3izGmOjHy1SQzcGQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "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" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/memoize": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.1.0.tgz", + "integrity": "sha512-MMbFhJzh4Jlg/poq1si90XRlTZRDHVqdlz2mPyGJ6kqMpyHUyVpDd5gpFAvVehW64+RA1eKE9Yt8aSLY7w2Kgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/memoize?sponsor=1" + } + }, + "node_modules/memoize-weak": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/memoize-weak/-/memoize-weak-1.0.2.tgz", + "integrity": "sha512-gj39xkrjEw7nCn4nJ1M5ms6+MyMlyiGmttzsqAUsAKn6bYKwuTHh/AO3cKPF8IBrTIYTxb0wWXFs3E//Y8VoWQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mode-watcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mode-watcher/-/mode-watcher-1.1.0.tgz", + "integrity": "sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "runed": "^0.25.0", + "svelte-toolbelt": "^0.7.1" + }, + "peerDependencies": { + "svelte": "^5.27.0" + } + }, + "node_modules/mode-watcher/node_modules/runed": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.25.0.tgz", + "integrity": "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, + "node_modules/mode-watcher/node_modules/svelte-toolbelt": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.7.1.tgz", + "integrity": "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte" + ], + "dependencies": { + "clsx": "^2.1.1", + "runed": "^0.23.2", + "style-to-object": "^1.0.8" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } + }, + "node_modules/mode-watcher/node_modules/svelte-toolbelt/node_modules/runed": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.23.4.tgz", + "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-url": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.2.tgz", + "integrity": "sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/paneforge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/paneforge/-/paneforge-1.0.1.tgz", + "integrity": "sha512-VQDeVsZ39Uc+CRvRFbhYzRAr08oDCE3Dor1l+9YG+IXrIrPrHg6E1CSXZ0KQitQNYu4Fec7BMh1SOXgg6rFrJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "runed": "^0.23.4", + "svelte-toolbelt": "^0.9.2" + }, + "peerDependencies": { + "svelte": "^5.29.0" + } + }, + "node_modules/paneforge/node_modules/runed": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.23.4.tgz", + "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-svelte": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.4.0.tgz", + "integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "prettier": "^3.0.0", + "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.6.14", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz", + "integrity": "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-hermes": "*", + "@prettier/plugin-oxc": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-multiline-arrays": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-hermes": { + "optional": true + }, + "@prettier/plugin-oxc": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-multiline-arrays": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/rollup": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/runed": { + "version": "0.29.2", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.29.2.tgz", + "integrity": "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "license": "MIT", + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/sirv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", + "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/style-to-object": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz", + "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, + "node_modules/superstruct": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", + "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svelte": { + "version": "5.37.1", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.37.1.tgz", + "integrity": "sha512-h8arWpQZ+3z8eahyBT5KkiBOUsG6xvI5Ykg0ozRr9xEdImgSMUPUlOFWRNkUsT7Ti0DSUCTEbPoped0aoxFyWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@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" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-check": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.0.tgz", + "integrity": "sha512-Iz8dFXzBNAM7XlEIsUjUGQhbEE+Pvv9odb9+0+ITTgFWZBGeJRRYqHUUglwe2EkLD5LIsQaAc4IUJyvtKuOO5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/svelte-sonner": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-1.0.5.tgz", + "integrity": "sha512-9dpGPFqKb/QWudYqGnEz93vuY+NgCEvyNvxoCLMVGw6sDN/3oVeKV1xiEirW2E1N3vJEyj5imSBNOGltQHA7mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "runed": "^0.28.0" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } + }, + "node_modules/svelte-sonner/node_modules/runed": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.28.0.tgz", + "integrity": "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "license": "MIT", + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, + "node_modules/svelte-toolbelt": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.9.3.tgz", + "integrity": "sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte" + ], + "dependencies": { + "clsx": "^2.1.1", + "runed": "^0.29.0", + "style-to-object": "^1.0.8" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "peerDependencies": { + "svelte": "^5.30.2" + } + }, + "node_modules/sveltekit-superforms": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/sveltekit-superforms/-/sveltekit-superforms-2.27.1.tgz", + "integrity": "sha512-cvq2AevkZ0Zrk0w0gNM3kjcnJMtJ0jzu+2zqDoM9a+lZa+8bGpNl4YqxVkemiJNkGnFgNC8xr5xF5BlMzjookQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ciscoheat" + }, + { + "type": "ko-fi", + "url": "https://ko-fi.com/ciscoheat" + }, + { + "type": "paypal", + "url": "https://www.paypal.com/donate/?hosted_button_id=NY7F5ALHHSVQS" + } + ], + "license": "MIT", + "dependencies": { + "devalue": "^5.1.1", + "memoize-weak": "^1.0.2", + "ts-deepmerge": "^7.0.3" + }, + "optionalDependencies": { + "@exodus/schemasafe": "^1.3.0", + "@gcornut/valibot-json-schema": "^0.42.0", + "@sinclair/typebox": "^0.34.35", + "@typeschema/class-validator": "^0.3.0", + "@vinejs/vine": "^3.0.1", + "arktype": "^2.1.20", + "class-validator": "^0.14.2", + "effect": "^3.16.7", + "joi": "^17.13.3", + "json-schema-to-ts": "^3.1.1", + "superstruct": "^2.0.2", + "valibot": "^1.1.0", + "yup": "^1.6.1", + "zod": "^3.25.64", + "zod-to-json-schema": "^3.24.5" + }, + "peerDependencies": { + "@exodus/schemasafe": "^1.3.0", + "@sinclair/typebox": "^0.34.28", + "@sveltejs/kit": "1.x || 2.x", + "@typeschema/class-validator": "^0.3.0", + "@vinejs/vine": "^1.8.0 || ^2.0.0 || ^3.0.0", + "arktype": ">=2.0.0-rc.23", + "class-validator": "^0.14.1", + "effect": "^3.13.7", + "joi": "^17.13.1", + "superstruct": "^2.0.2", + "svelte": "3.x || 4.x || >=5.0.0-next.51", + "valibot": "^1.0.0", + "yup": "^1.4.0", + "zod": "^3.25.0" + }, + "peerDependenciesMeta": { + "@exodus/schemasafe": { + "optional": true + }, + "@sinclair/typebox": { + "optional": true + }, + "@typeschema/class-validator": { + "optional": true + }, + "@vinejs/vine": { + "optional": true + }, + "arktype": { + "optional": true + }, + "class-validator": { + "optional": true + }, + "effect": { + "optional": true + }, + "joi": { + "optional": true + }, + "superstruct": { + "optional": true + }, + "valibot": { + "optional": true + }, + "yup": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true, + "license": "MIT" + }, + "node_modules/tailwind-merge": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwind-variants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-1.0.0.tgz", + "integrity": "sha512-2WSbv4ulEEyuBKomOunut65D8UZwxrHoRfYnxGcQNnHqlSCp2+B7Yz2W+yrNDrxRodOXtGD/1oCcKGNBnUqMqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tailwind-merge": "3.0.2" + }, + "engines": { + "node": ">=16.x", + "pnpm": ">=7.x" + }, + "peerDependencies": { + "tailwindcss": "*" + } + }, + "node_modules/tailwind-variants/node_modules/tailwind-merge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz", + "integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/ts-deepmerge": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-7.0.3.tgz", + "integrity": "sha512-Du/ZW2RfwV/D4cmA5rXafYjBQVuvu4qGiEEla4EmEHVHgRdx68Gftx7i66jn2bzHPwSVZY36Ae6OuDn9el4ZKA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14.13.1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tw-animate-css": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.6.tgz", + "integrity": "sha512-9dy0R9UsYEGmgf26L8UcHiLmSFTHa9+D7+dAt/G/sF5dCnPePZbfgDYinc7/UzAM7g/baVrmS6m9yEpU46d+LA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT" + }, + "node_modules/valibot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.1.0.tgz", + "integrity": "sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw==", + "dev": true, + "license": "MIT", + "optional": true, + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/validator": { + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vaul-svelte": { + "version": "1.0.0-next.7", + "resolved": "https://registry.npmjs.org/vaul-svelte/-/vaul-svelte-1.0.0-next.7.tgz", + "integrity": "sha512-7zN7Bi3dFQixvvbUJY9uGDe7Ws/dGZeBQR2pXdXmzQiakjrxBvWo0QrmsX3HK+VH+SZOltz378cmgmCS9f9rSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "runed": "^0.23.2", + "svelte-toolbelt": "^0.7.1" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } + }, + "node_modules/vaul-svelte/node_modules/runed": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.23.4.tgz", + "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, + "node_modules/vaul-svelte/node_modules/svelte-toolbelt": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.7.1.tgz", + "integrity": "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte" + ], + "dependencies": { + "clsx": "^2.1.1", + "runed": "^0.23.2", + "style-to-object": "^1.0.8" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } + }, + "node_modules/vite": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", + "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-parser/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yup": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz", + "integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/zimmerframe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", + "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "dev": true, + "license": "ISC", + "optional": true, + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..c9140bc --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,53 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "start": "node build/index.js", + "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", + "format": "prettier --write .", + "lint": "prettier --check ." + }, + "devDependencies": { + "@internationalized/date": "^3.8.2", + "@lucide/svelte": "^0.515.0", + "@sveltejs/adapter-auto": "^6.0.1", + "@sveltejs/adapter-node": "^5.2.13", + "@sveltejs/kit": "^2.26.1", + "@sveltejs/vite-plugin-svelte": "^6.1.0", + "@tailwindcss/vite": "^4.1.11", + "@tanstack/table-core": "^8.21.3", + "bits-ui": "^2.9.0", + "clsx": "^2.1.1", + "embla-carousel-svelte": "^8.6.0", + "formsnap": "^2.0.1", + "layerchart": "^2.0.0-next.27", + "mode-watcher": "^1.1.0", + "paneforge": "^1.0.1", + "prettier": "^3.6.2", + "prettier-plugin-svelte": "^3.4.0", + "prettier-plugin-tailwindcss": "^0.6.14", + "svelte": "^5.37.1", + "svelte-check": "^4.3.0", + "svelte-sonner": "^1.0.5", + "sveltekit-superforms": "^2.27.1", + "tailwind-merge": "^3.3.1", + "tailwind-variants": "^1.0.0", + "tailwindcss": "^4.1.11", + "tw-animate-css": "^1.3.6", + "typescript": "^5.9.2", + "vaul-svelte": "^1.0.0-next.7", + "vite": "^7.0.6" + }, + "dependencies": { + "@supabase/supabase-js": "^2.53.0", + "@types/qrcode": "^1.5.5", + "qrcode": "^1.5.4" + } +} diff --git a/frontend/src/app.css b/frontend/src/app.css new file mode 100644 index 0000000..cf458d9 --- /dev/null +++ b/frontend/src/app.css @@ -0,0 +1,128 @@ +@import 'tailwindcss'; + +@import 'tw-animate-css'; + +@custom-variant dark (&:is(.dark *)); + +* { + scrollbar-color: color-mix(in oklch, currentColor 35%, transparent) transparent; +} +*:hover { + scrollbar-color: color-mix(in oklch, currentColor 60%, transparent) transparent; +} + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.21 0.006 285.885); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.705 0.015 286.067); + --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.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.21 0.006 285.885); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.705 0.015 286.067); +} + +.dark { + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.92 0.004 286.32); + --primary-foreground: oklch(0.21 0.006 285.885); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.552 0.016 285.938); + --chart-1: oklch(0.488 0.243 264.376); + --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.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.552 0.016 285.938); +} + +@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; + } +} diff --git a/frontend/src/app.d.ts b/frontend/src/app.d.ts new file mode 100644 index 0000000..d76242a --- /dev/null +++ b/frontend/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/frontend/src/app.html b/frontend/src/app.html new file mode 100644 index 0000000..53c0607 --- /dev/null +++ b/frontend/src/app.html @@ -0,0 +1,14 @@ + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/frontend/src/lib/assets/favicon.svg b/frontend/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/frontend/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/frontend/src/lib/components/app-sidebar.svelte b/frontend/src/lib/components/app-sidebar.svelte new file mode 100644 index 0000000..c0ad410 --- /dev/null +++ b/frontend/src/lib/components/app-sidebar.svelte @@ -0,0 +1,171 @@ + + + + + + + + + + + + {#each data.navMain as group (group.title)} + + {group.title} + + + {#each group.items as item (item.title)} + + + {#snippet child({ props })} + {item.title} + {/snippet} + + + {/each} + + + + {/each} + + + diff --git a/frontend/src/lib/components/search-form.svelte b/frontend/src/lib/components/search-form.svelte new file mode 100644 index 0000000..e46557b --- /dev/null +++ b/frontend/src/lib/components/search-form.svelte @@ -0,0 +1,21 @@ + + +
+ + + + + + + +
diff --git a/frontend/src/lib/components/ui/accordion/accordion-content.svelte b/frontend/src/lib/components/ui/accordion/accordion-content.svelte new file mode 100644 index 0000000..684566a --- /dev/null +++ b/frontend/src/lib/components/ui/accordion/accordion-content.svelte @@ -0,0 +1,22 @@ + + + +
+ {@render children?.()} +
+
diff --git a/frontend/src/lib/components/ui/accordion/accordion-item.svelte b/frontend/src/lib/components/ui/accordion/accordion-item.svelte new file mode 100644 index 0000000..726dc9a --- /dev/null +++ b/frontend/src/lib/components/ui/accordion/accordion-item.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/accordion/accordion-trigger.svelte b/frontend/src/lib/components/ui/accordion/accordion-trigger.svelte new file mode 100644 index 0000000..a7d4ad3 --- /dev/null +++ b/frontend/src/lib/components/ui/accordion/accordion-trigger.svelte @@ -0,0 +1,32 @@ + + + + svg]:rotate-180', + className + )} + {...restProps} + > + {@render children?.()} + + + diff --git a/frontend/src/lib/components/ui/accordion/accordion.svelte b/frontend/src/lib/components/ui/accordion/accordion.svelte new file mode 100644 index 0000000..6aa583c --- /dev/null +++ b/frontend/src/lib/components/ui/accordion/accordion.svelte @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/lib/components/ui/accordion/index.ts b/frontend/src/lib/components/ui/accordion/index.ts new file mode 100644 index 0000000..0b1160b --- /dev/null +++ b/frontend/src/lib/components/ui/accordion/index.ts @@ -0,0 +1,16 @@ +import Root from './accordion.svelte'; +import Content from './accordion-content.svelte'; +import Item from './accordion-item.svelte'; +import Trigger from './accordion-trigger.svelte'; + +export { + Root, + Content, + Item, + Trigger, + // + Root as Accordion, + Content as AccordionContent, + Item as AccordionItem, + Trigger as AccordionTrigger +}; diff --git a/frontend/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte new file mode 100644 index 0000000..58d1978 --- /dev/null +++ b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte @@ -0,0 +1,18 @@ + + + diff --git a/frontend/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte new file mode 100644 index 0000000..eb2078c --- /dev/null +++ b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte @@ -0,0 +1,18 @@ + + + diff --git a/frontend/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte new file mode 100644 index 0000000..e0f5dd9 --- /dev/null +++ b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte @@ -0,0 +1,27 @@ + + + + + + diff --git a/frontend/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte new file mode 100644 index 0000000..2a229fb --- /dev/null +++ b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte new file mode 100644 index 0000000..ea9d9b0 --- /dev/null +++ b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/frontend/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte new file mode 100644 index 0000000..25f2534 --- /dev/null +++ b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/frontend/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte new file mode 100644 index 0000000..a8ac08b --- /dev/null +++ b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte new file mode 100644 index 0000000..5deaae7 --- /dev/null +++ b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte new file mode 100644 index 0000000..ac1ece3 --- /dev/null +++ b/frontend/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/alert-dialog/index.ts b/frontend/src/lib/components/ui/alert-dialog/index.ts new file mode 100644 index 0000000..b538b41 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/alert/alert-description.svelte b/frontend/src/lib/components/ui/alert/alert-description.svelte new file mode 100644 index 0000000..6f15333 --- /dev/null +++ b/frontend/src/lib/components/ui/alert/alert-description.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/frontend/src/lib/components/ui/alert/alert-title.svelte b/frontend/src/lib/components/ui/alert/alert-title.svelte new file mode 100644 index 0000000..175561d --- /dev/null +++ b/frontend/src/lib/components/ui/alert/alert-title.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/frontend/src/lib/components/ui/alert/alert.svelte b/frontend/src/lib/components/ui/alert/alert.svelte new file mode 100644 index 0000000..8a0a076 --- /dev/null +++ b/frontend/src/lib/components/ui/alert/alert.svelte @@ -0,0 +1,44 @@ + + + + + diff --git a/frontend/src/lib/components/ui/alert/index.ts b/frontend/src/lib/components/ui/alert/index.ts new file mode 100644 index 0000000..7e1c6cd --- /dev/null +++ b/frontend/src/lib/components/ui/alert/index.ts @@ -0,0 +1,14 @@ +import Root from './alert.svelte'; +import Description from './alert-description.svelte'; +import Title from './alert-title.svelte'; +export { alertVariants, type AlertVariant } from './alert.svelte'; + +export { + Root, + Description, + Title, + // + Root as Alert, + Description as AlertDescription, + Title as AlertTitle +}; diff --git a/frontend/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte b/frontend/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte new file mode 100644 index 0000000..c251e65 --- /dev/null +++ b/frontend/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/aspect-ratio/index.ts b/frontend/src/lib/components/ui/aspect-ratio/index.ts new file mode 100644 index 0000000..1d9517e --- /dev/null +++ b/frontend/src/lib/components/ui/aspect-ratio/index.ts @@ -0,0 +1,3 @@ +import Root from './aspect-ratio.svelte'; + +export { Root, Root as AspectRatio }; diff --git a/frontend/src/lib/components/ui/avatar/avatar-fallback.svelte b/frontend/src/lib/components/ui/avatar/avatar-fallback.svelte new file mode 100644 index 0000000..fd07889 --- /dev/null +++ b/frontend/src/lib/components/ui/avatar/avatar-fallback.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/avatar/avatar-image.svelte b/frontend/src/lib/components/ui/avatar/avatar-image.svelte new file mode 100644 index 0000000..ce3eff7 --- /dev/null +++ b/frontend/src/lib/components/ui/avatar/avatar-image.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/avatar/avatar.svelte b/frontend/src/lib/components/ui/avatar/avatar.svelte new file mode 100644 index 0000000..f663840 --- /dev/null +++ b/frontend/src/lib/components/ui/avatar/avatar.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/avatar/index.ts b/frontend/src/lib/components/ui/avatar/index.ts new file mode 100644 index 0000000..808d862 --- /dev/null +++ b/frontend/src/lib/components/ui/avatar/index.ts @@ -0,0 +1,13 @@ +import Root from './avatar.svelte'; +import Image from './avatar-image.svelte'; +import Fallback from './avatar-fallback.svelte'; + +export { + Root, + Image, + Fallback, + // + Root as Avatar, + Image as AvatarImage, + Fallback as AvatarFallback +}; diff --git a/frontend/src/lib/components/ui/badge/badge.svelte b/frontend/src/lib/components/ui/badge/badge.svelte new file mode 100644 index 0000000..9ff2c2a --- /dev/null +++ b/frontend/src/lib/components/ui/badge/badge.svelte @@ -0,0 +1,50 @@ + + + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/badge/index.ts b/frontend/src/lib/components/ui/badge/index.ts new file mode 100644 index 0000000..f05fb87 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte new file mode 100644 index 0000000..1ff117b --- /dev/null +++ b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte @@ -0,0 +1,23 @@ + + + diff --git a/frontend/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte new file mode 100644 index 0000000..6a0fc05 --- /dev/null +++ b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte @@ -0,0 +1,20 @@ + + +
  • + {@render children?.()} +
  • diff --git a/frontend/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte new file mode 100644 index 0000000..17a1d08 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte new file mode 100644 index 0000000..1404f1c --- /dev/null +++ b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte @@ -0,0 +1,23 @@ + + +
      + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte new file mode 100644 index 0000000..11eee20 --- /dev/null +++ b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte new file mode 100644 index 0000000..df58e5f --- /dev/null +++ b/frontend/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte @@ -0,0 +1,27 @@ + + + diff --git a/frontend/src/lib/components/ui/breadcrumb/breadcrumb.svelte b/frontend/src/lib/components/ui/breadcrumb/breadcrumb.svelte new file mode 100644 index 0000000..ab19b8c --- /dev/null +++ b/frontend/src/lib/components/ui/breadcrumb/breadcrumb.svelte @@ -0,0 +1,21 @@ + + + diff --git a/frontend/src/lib/components/ui/breadcrumb/index.ts b/frontend/src/lib/components/ui/breadcrumb/index.ts new file mode 100644 index 0000000..0db2bac --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/button/button.svelte b/frontend/src/lib/components/ui/button/button.svelte new file mode 100644 index 0000000..50a4ab7 --- /dev/null +++ b/frontend/src/lib/components/ui/button/button.svelte @@ -0,0 +1,80 @@ + + + + +{#if href} + + {@render children?.()} + +{:else} + +{/if} diff --git a/frontend/src/lib/components/ui/button/index.ts b/frontend/src/lib/components/ui/button/index.ts new file mode 100644 index 0000000..af28e18 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/calendar/calendar-caption.svelte b/frontend/src/lib/components/ui/calendar/calendar-caption.svelte new file mode 100644 index 0000000..c58c1b9 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-caption.svelte @@ -0,0 +1,76 @@ + + +{#snippet MonthSelect()} + { + if (!placeholder) return; + const v = Number.parseInt(e.currentTarget.value); + const newPlaceholder = placeholder.set({ month: v }); + placeholder = newPlaceholder.subtract({ months: monthIndex }); + }} + /> +{/snippet} + +{#snippet YearSelect()} + +{/snippet} + +{#if captionLayout === 'dropdown'} + {@render MonthSelect()} + {@render YearSelect()} +{:else if captionLayout === 'dropdown-months'} + {@render MonthSelect()} + {#if placeholder} + {formatYear(placeholder)} + {/if} +{:else if captionLayout === 'dropdown-years'} + {#if placeholder} + {formatMonth(placeholder)} + {/if} + {@render YearSelect()} +{:else} + {formatMonth(month)} {formatYear(month)} +{/if} diff --git a/frontend/src/lib/components/ui/calendar/calendar-cell.svelte b/frontend/src/lib/components/ui/calendar/calendar-cell.svelte new file mode 100644 index 0000000..c7b5001 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-cell.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-day.svelte b/frontend/src/lib/components/ui/calendar/calendar-day.svelte new file mode 100644 index 0000000..aec1817 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-day.svelte @@ -0,0 +1,35 @@ + + +span]:text-xs [&>span]:opacity-70', + className + )} + {...restProps} +/> diff --git a/frontend/src/lib/components/ui/calendar/calendar-grid-body.svelte b/frontend/src/lib/components/ui/calendar/calendar-grid-body.svelte new file mode 100644 index 0000000..a01297c --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-grid-body.svelte @@ -0,0 +1,12 @@ + + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-grid-head.svelte b/frontend/src/lib/components/ui/calendar/calendar-grid-head.svelte new file mode 100644 index 0000000..c533959 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-grid-head.svelte @@ -0,0 +1,12 @@ + + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-grid-row.svelte b/frontend/src/lib/components/ui/calendar/calendar-grid-row.svelte new file mode 100644 index 0000000..fd03d65 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-grid-row.svelte @@ -0,0 +1,12 @@ + + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-grid.svelte b/frontend/src/lib/components/ui/calendar/calendar-grid.svelte new file mode 100644 index 0000000..5c36d29 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-grid.svelte @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-head-cell.svelte b/frontend/src/lib/components/ui/calendar/calendar-head-cell.svelte new file mode 100644 index 0000000..352380d --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-head-cell.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-header.svelte b/frontend/src/lib/components/ui/calendar/calendar-header.svelte new file mode 100644 index 0000000..9303ba2 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-header.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-heading.svelte b/frontend/src/lib/components/ui/calendar/calendar-heading.svelte new file mode 100644 index 0000000..e0b66ca --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-heading.svelte @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-month-select.svelte b/frontend/src/lib/components/ui/calendar/calendar-month-select.svelte new file mode 100644 index 0000000..99d9aef --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-month-select.svelte @@ -0,0 +1,44 @@ + + + + + {#snippet child({ props, monthItems, selectedMonthItem })} + + + {/snippet} + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-month.svelte b/frontend/src/lib/components/ui/calendar/calendar-month.svelte new file mode 100644 index 0000000..0fe50c6 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-month.svelte @@ -0,0 +1,15 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/calendar/calendar-months.svelte b/frontend/src/lib/components/ui/calendar/calendar-months.svelte new file mode 100644 index 0000000..21a914b --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-months.svelte @@ -0,0 +1,19 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/calendar/calendar-nav.svelte b/frontend/src/lib/components/ui/calendar/calendar-nav.svelte new file mode 100644 index 0000000..cd5498f --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-nav.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-next-button.svelte b/frontend/src/lib/components/ui/calendar/calendar-next-button.svelte new file mode 100644 index 0000000..3140292 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-next-button.svelte @@ -0,0 +1,31 @@ + + +{#snippet Fallback()} + +{/snippet} + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-prev-button.svelte b/frontend/src/lib/components/ui/calendar/calendar-prev-button.svelte new file mode 100644 index 0000000..b67fa1d --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-prev-button.svelte @@ -0,0 +1,31 @@ + + +{#snippet Fallback()} + +{/snippet} + + diff --git a/frontend/src/lib/components/ui/calendar/calendar-year-select.svelte b/frontend/src/lib/components/ui/calendar/calendar-year-select.svelte new file mode 100644 index 0000000..54ffeed --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar-year-select.svelte @@ -0,0 +1,43 @@ + + + + + {#snippet child({ props, yearItems, selectedYearItem })} + + + {/snippet} + + diff --git a/frontend/src/lib/components/ui/calendar/calendar.svelte b/frontend/src/lib/components/ui/calendar/calendar.svelte new file mode 100644 index 0000000..8a60597 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/calendar.svelte @@ -0,0 +1,115 @@ + + + + + {#snippet children({ months, weekdays })} + + + + + + {#each months as month, monthIndex (month)} + + + + + + + + {#each weekdays as weekday (weekday)} + + {weekday.slice(0, 2)} + + {/each} + + + + {#each month.weeks as weekDates (weekDates)} + + {#each weekDates as date (date)} + + {#if day} + {@render day({ + day: date, + outsideMonth: !isEqualMonth(date, month.value) + })} + {:else} + + {/if} + + {/each} + + {/each} + + + + {/each} + + {/snippet} + diff --git a/frontend/src/lib/components/ui/calendar/index.ts b/frontend/src/lib/components/ui/calendar/index.ts new file mode 100644 index 0000000..9b4c8d2 --- /dev/null +++ b/frontend/src/lib/components/ui/calendar/index.ts @@ -0,0 +1,40 @@ +import Root from './calendar.svelte'; +import Cell from './calendar-cell.svelte'; +import Day from './calendar-day.svelte'; +import Grid from './calendar-grid.svelte'; +import Header from './calendar-header.svelte'; +import Months from './calendar-months.svelte'; +import GridRow from './calendar-grid-row.svelte'; +import Heading from './calendar-heading.svelte'; +import GridBody from './calendar-grid-body.svelte'; +import GridHead from './calendar-grid-head.svelte'; +import HeadCell from './calendar-head-cell.svelte'; +import NextButton from './calendar-next-button.svelte'; +import PrevButton from './calendar-prev-button.svelte'; +import MonthSelect from './calendar-month-select.svelte'; +import YearSelect from './calendar-year-select.svelte'; +import Month from './calendar-month.svelte'; +import Nav from './calendar-nav.svelte'; +import Caption from './calendar-caption.svelte'; + +export { + Day, + Cell, + Grid, + Header, + Months, + GridRow, + Heading, + GridBody, + GridHead, + HeadCell, + NextButton, + PrevButton, + Nav, + Month, + YearSelect, + MonthSelect, + Caption, + // + Root as Calendar +}; diff --git a/frontend/src/lib/components/ui/card/card-action.svelte b/frontend/src/lib/components/ui/card/card-action.svelte new file mode 100644 index 0000000..0231a52 --- /dev/null +++ b/frontend/src/lib/components/ui/card/card-action.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/card/card-content.svelte b/frontend/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000..6160e88 --- /dev/null +++ b/frontend/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,15 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/card/card-description.svelte b/frontend/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000..d43f575 --- /dev/null +++ b/frontend/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,20 @@ + + +

    + {@render children?.()} +

    diff --git a/frontend/src/lib/components/ui/card/card-footer.svelte b/frontend/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000..4b55730 --- /dev/null +++ b/frontend/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/card/card-header.svelte b/frontend/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000..11a2f23 --- /dev/null +++ b/frontend/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/card/card-title.svelte b/frontend/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000..52490fd --- /dev/null +++ b/frontend/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/card/card.svelte b/frontend/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000..4a3e760 --- /dev/null +++ b/frontend/src/lib/components/ui/card/card.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/card/index.ts b/frontend/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000..b4af293 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/carousel/carousel-content.svelte b/frontend/src/lib/components/ui/carousel/carousel-content.svelte new file mode 100644 index 0000000..1086f85 --- /dev/null +++ b/frontend/src/lib/components/ui/carousel/carousel-content.svelte @@ -0,0 +1,43 @@ + + +
    +
    + {@render children?.()} +
    +
    diff --git a/frontend/src/lib/components/ui/carousel/carousel-item.svelte b/frontend/src/lib/components/ui/carousel/carousel-item.svelte new file mode 100644 index 0000000..2a9c1c7 --- /dev/null +++ b/frontend/src/lib/components/ui/carousel/carousel-item.svelte @@ -0,0 +1,30 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/carousel/carousel-next.svelte b/frontend/src/lib/components/ui/carousel/carousel-next.svelte new file mode 100644 index 0000000..2de0251 --- /dev/null +++ b/frontend/src/lib/components/ui/carousel/carousel-next.svelte @@ -0,0 +1,38 @@ + + + diff --git a/frontend/src/lib/components/ui/carousel/carousel-previous.svelte b/frontend/src/lib/components/ui/carousel/carousel-previous.svelte new file mode 100644 index 0000000..47584fa --- /dev/null +++ b/frontend/src/lib/components/ui/carousel/carousel-previous.svelte @@ -0,0 +1,38 @@ + + + diff --git a/frontend/src/lib/components/ui/carousel/carousel.svelte b/frontend/src/lib/components/ui/carousel/carousel.svelte new file mode 100644 index 0000000..3ae60e6 --- /dev/null +++ b/frontend/src/lib/components/ui/carousel/carousel.svelte @@ -0,0 +1,93 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/carousel/context.ts b/frontend/src/lib/components/ui/carousel/context.ts new file mode 100644 index 0000000..e92ef0a --- /dev/null +++ b/frontend/src/lib/components/ui/carousel/context.ts @@ -0,0 +1,58 @@ +import type { WithElementRef } from '$lib/utils.js'; +import type { + EmblaCarouselSvelteType, + default as emblaCarouselSvelte +} from 'embla-carousel-svelte'; +import { getContext, hasContext, setContext } from 'svelte'; +import type { HTMLAttributes } from 'svelte/elements'; + +export type CarouselAPI = + NonNullable['on:emblaInit']> extends ( + evt: CustomEvent + ) => void + ? CarouselAPI + : never; + +type EmblaCarouselConfig = NonNullable[1]>; + +export type CarouselOptions = EmblaCarouselConfig['options']; +export type CarouselPlugins = EmblaCarouselConfig['plugins']; + +//// + +export type CarouselProps = { + opts?: CarouselOptions; + plugins?: CarouselPlugins; + setApi?: (api: CarouselAPI | undefined) => void; + orientation?: 'horizontal' | 'vertical'; +} & WithElementRef>; + +const EMBLA_CAROUSEL_CONTEXT = Symbol('EMBLA_CAROUSEL_CONTEXT'); + +export type EmblaContext = { + api: CarouselAPI | undefined; + orientation: 'horizontal' | 'vertical'; + scrollNext: () => void; + scrollPrev: () => void; + canScrollNext: boolean; + canScrollPrev: boolean; + handleKeyDown: (e: KeyboardEvent) => void; + options: CarouselOptions; + plugins: CarouselPlugins; + onInit: (e: CustomEvent) => void; + scrollTo: (index: number, jump?: boolean) => void; + scrollSnaps: number[]; + selectedIndex: number; +}; + +export function setEmblaContext(config: EmblaContext): EmblaContext { + setContext(EMBLA_CAROUSEL_CONTEXT, config); + return config; +} + +export function getEmblaContext(name = 'This component') { + if (!hasContext(EMBLA_CAROUSEL_CONTEXT)) { + throw new Error(`${name} must be used within a component`); + } + return getContext>(EMBLA_CAROUSEL_CONTEXT); +} diff --git a/frontend/src/lib/components/ui/carousel/index.ts b/frontend/src/lib/components/ui/carousel/index.ts new file mode 100644 index 0000000..acd3873 --- /dev/null +++ b/frontend/src/lib/components/ui/carousel/index.ts @@ -0,0 +1,19 @@ +import Root from './carousel.svelte'; +import Content from './carousel-content.svelte'; +import Item from './carousel-item.svelte'; +import Previous from './carousel-previous.svelte'; +import Next from './carousel-next.svelte'; + +export { + Root, + Content, + Item, + Previous, + Next, + // + Root as Carousel, + Content as CarouselContent, + Item as CarouselItem, + Previous as CarouselPrevious, + Next as CarouselNext +}; diff --git a/frontend/src/lib/components/ui/chart/chart-container.svelte b/frontend/src/lib/components/ui/chart/chart-container.svelte new file mode 100644 index 0000000..c77a48b --- /dev/null +++ b/frontend/src/lib/components/ui/chart/chart-container.svelte @@ -0,0 +1,80 @@ + + +
    + + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/chart/chart-style.svelte b/frontend/src/lib/components/ui/chart/chart-style.svelte new file mode 100644 index 0000000..a67d961 --- /dev/null +++ b/frontend/src/lib/components/ui/chart/chart-style.svelte @@ -0,0 +1,37 @@ + + +{#if themeContents} + {#key id} + + {themeContents} + + {/key} +{/if} diff --git a/frontend/src/lib/components/ui/chart/chart-tooltip.svelte b/frontend/src/lib/components/ui/chart/chart-tooltip.svelte new file mode 100644 index 0000000..7cc2bae --- /dev/null +++ b/frontend/src/lib/components/ui/chart/chart-tooltip.svelte @@ -0,0 +1,159 @@ + + +{#snippet TooltipLabel()} + {#if formattedLabel} +
    + {#if typeof formattedLabel === 'function'} + {@render formattedLabel()} + {:else} + {formattedLabel} + {/if} +
    + {/if} +{/snippet} + + +
    + {#if !nestLabel} + {@render TooltipLabel()} + {/if} +
    + {#each tooltipCtx.payload as item, i (item.key + i)} + {@const key = `${nameKey || item.key || item.name || 'value'}`} + {@const itemConfig = getPayloadConfigFromPayload(chart.config, item, key)} + {@const indicatorColor = color || item.payload?.color || item.color} +
    svg]:size-2.5 [&>svg]:text-muted-foreground', + indicator === 'dot' && 'items-center' + )} + > + {#if formatter && item.value !== undefined && item.name} + {@render formatter({ + value: item.value, + name: item.name, + item, + index: i, + payload: tooltipCtx.payload + })} + {:else} + {#if itemConfig?.icon} + + {:else if !hideIndicator} +
    + {/if} +
    +
    + {#if nestLabel} + {@render TooltipLabel()} + {/if} + + {itemConfig?.label || item.name} + +
    + {#if item.value !== undefined} + + {item.value.toLocaleString()} + + {/if} +
    + {/if} +
    + {/each} +
    +
    +
    diff --git a/frontend/src/lib/components/ui/chart/chart-utils.ts b/frontend/src/lib/components/ui/chart/chart-utils.ts new file mode 100644 index 0000000..9ff232f --- /dev/null +++ b/frontend/src/lib/components/ui/chart/chart-utils.ts @@ -0,0 +1,66 @@ +import type { Tooltip } from 'layerchart'; +import { getContext, setContext, type Component, type ComponentProps, type Snippet } from 'svelte'; + +export const THEMES = { light: '', dark: '.dark' } as const; + +export type ChartConfig = { + [k in string]: { + label?: string; + icon?: Component; + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ); +}; + +export type ExtractSnippetParams = T extends Snippet<[infer P]> ? P : never; + +export type TooltipPayload = ExtractSnippetParams< + ComponentProps['children'] +>['payload'][number]; + +// Helper to extract item config from a payload. +export function getPayloadConfigFromPayload( + config: ChartConfig, + payload: TooltipPayload, + key: string +) { + if (typeof payload !== 'object' || payload === null) return undefined; + + const payloadPayload = + 'payload' in payload && typeof payload.payload === 'object' && payload.payload !== null + ? payload.payload + : undefined; + + let configLabelKey: string = key; + + if (payload.key === key) { + configLabelKey = payload.key; + } else if (payload.name === key) { + configLabelKey = payload.name; + } else if (key in payload && typeof payload[key as keyof typeof payload] === 'string') { + configLabelKey = payload[key as keyof typeof payload] as string; + } else if ( + payloadPayload !== undefined && + key in payloadPayload && + typeof payloadPayload[key as keyof typeof payloadPayload] === 'string' + ) { + configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string; + } + + return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config]; +} + +type ChartContextValue = { + config: ChartConfig; +}; + +const chartContextKey = Symbol('chart-context'); + +export function setChartContext(value: ChartContextValue) { + return setContext(chartContextKey, value); +} + +export function useChart() { + return getContext(chartContextKey); +} diff --git a/frontend/src/lib/components/ui/chart/index.ts b/frontend/src/lib/components/ui/chart/index.ts new file mode 100644 index 0000000..25cfcd4 --- /dev/null +++ b/frontend/src/lib/components/ui/chart/index.ts @@ -0,0 +1,6 @@ +import ChartContainer from './chart-container.svelte'; +import ChartTooltip from './chart-tooltip.svelte'; + +export { getPayloadConfigFromPayload, type ChartConfig } from './chart-utils.js'; + +export { ChartContainer, ChartTooltip, ChartContainer as Container, ChartTooltip as Tooltip }; diff --git a/frontend/src/lib/components/ui/checkbox/checkbox.svelte b/frontend/src/lib/components/ui/checkbox/checkbox.svelte new file mode 100644 index 0000000..1b5e720 --- /dev/null +++ b/frontend/src/lib/components/ui/checkbox/checkbox.svelte @@ -0,0 +1,36 @@ + + + + {#snippet children({ checked, indeterminate })} +
    + {#if checked} + + {:else if indeterminate} + + {/if} +
    + {/snippet} +
    diff --git a/frontend/src/lib/components/ui/checkbox/index.ts b/frontend/src/lib/components/ui/checkbox/index.ts new file mode 100644 index 0000000..1fad96b --- /dev/null +++ b/frontend/src/lib/components/ui/checkbox/index.ts @@ -0,0 +1,6 @@ +import Root from './checkbox.svelte'; +export { + Root, + // + Root as Checkbox +}; diff --git a/frontend/src/lib/components/ui/collapsible/collapsible-content.svelte b/frontend/src/lib/components/ui/collapsible/collapsible-content.svelte new file mode 100644 index 0000000..da1ec4c --- /dev/null +++ b/frontend/src/lib/components/ui/collapsible/collapsible-content.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/collapsible/collapsible-trigger.svelte b/frontend/src/lib/components/ui/collapsible/collapsible-trigger.svelte new file mode 100644 index 0000000..c51e4be --- /dev/null +++ b/frontend/src/lib/components/ui/collapsible/collapsible-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/collapsible/collapsible.svelte b/frontend/src/lib/components/ui/collapsible/collapsible.svelte new file mode 100644 index 0000000..d4f3a59 --- /dev/null +++ b/frontend/src/lib/components/ui/collapsible/collapsible.svelte @@ -0,0 +1,11 @@ + + + diff --git a/frontend/src/lib/components/ui/collapsible/index.ts b/frontend/src/lib/components/ui/collapsible/index.ts new file mode 100644 index 0000000..4f1ea90 --- /dev/null +++ b/frontend/src/lib/components/ui/collapsible/index.ts @@ -0,0 +1,13 @@ +import Root from './collapsible.svelte'; +import Trigger from './collapsible-trigger.svelte'; +import Content from './collapsible-content.svelte'; + +export { + Root, + Content, + Trigger, + // + Root as Collapsible, + Content as CollapsibleContent, + Trigger as CollapsibleTrigger +}; diff --git a/frontend/src/lib/components/ui/command/command-dialog.svelte b/frontend/src/lib/components/ui/command/command-dialog.svelte new file mode 100644 index 0000000..fa6cb8f --- /dev/null +++ b/frontend/src/lib/components/ui/command/command-dialog.svelte @@ -0,0 +1,40 @@ + + + + + {title} + {description} + + + + + diff --git a/frontend/src/lib/components/ui/command/command-empty.svelte b/frontend/src/lib/components/ui/command/command-empty.svelte new file mode 100644 index 0000000..b722a9a --- /dev/null +++ b/frontend/src/lib/components/ui/command/command-empty.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/command/command-group.svelte b/frontend/src/lib/components/ui/command/command-group.svelte new file mode 100644 index 0000000..68eaeee --- /dev/null +++ b/frontend/src/lib/components/ui/command/command-group.svelte @@ -0,0 +1,32 @@ + + + + {#if heading} + + {heading} + + {/if} + + diff --git a/frontend/src/lib/components/ui/command/command-input.svelte b/frontend/src/lib/components/ui/command/command-input.svelte new file mode 100644 index 0000000..7c6a4c7 --- /dev/null +++ b/frontend/src/lib/components/ui/command/command-input.svelte @@ -0,0 +1,26 @@ + + +
    + + +
    diff --git a/frontend/src/lib/components/ui/command/command-item.svelte b/frontend/src/lib/components/ui/command/command-item.svelte new file mode 100644 index 0000000..da86427 --- /dev/null +++ b/frontend/src/lib/components/ui/command/command-item.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/command/command-link-item.svelte b/frontend/src/lib/components/ui/command/command-link-item.svelte new file mode 100644 index 0000000..2a271c9 --- /dev/null +++ b/frontend/src/lib/components/ui/command/command-link-item.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/command/command-list.svelte b/frontend/src/lib/components/ui/command/command-list.svelte new file mode 100644 index 0000000..63f68d8 --- /dev/null +++ b/frontend/src/lib/components/ui/command/command-list.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/command/command-separator.svelte b/frontend/src/lib/components/ui/command/command-separator.svelte new file mode 100644 index 0000000..284b7b1 --- /dev/null +++ b/frontend/src/lib/components/ui/command/command-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/command/command-shortcut.svelte b/frontend/src/lib/components/ui/command/command-shortcut.svelte new file mode 100644 index 0000000..ca8729c --- /dev/null +++ b/frontend/src/lib/components/ui/command/command-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/command/command.svelte b/frontend/src/lib/components/ui/command/command.svelte new file mode 100644 index 0000000..ad452b5 --- /dev/null +++ b/frontend/src/lib/components/ui/command/command.svelte @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/lib/components/ui/command/index.ts b/frontend/src/lib/components/ui/command/index.ts new file mode 100644 index 0000000..301fa6e --- /dev/null +++ b/frontend/src/lib/components/ui/command/index.ts @@ -0,0 +1,40 @@ +import { Command as CommandPrimitive } from 'bits-ui'; + +import Root from './command.svelte'; +import Dialog from './command-dialog.svelte'; +import Empty from './command-empty.svelte'; +import Group from './command-group.svelte'; +import Item from './command-item.svelte'; +import Input from './command-input.svelte'; +import List from './command-list.svelte'; +import Separator from './command-separator.svelte'; +import Shortcut from './command-shortcut.svelte'; +import LinkItem from './command-link-item.svelte'; + +const Loading = CommandPrimitive.Loading; + +export { + Root, + Dialog, + Empty, + Group, + Item, + LinkItem, + Input, + List, + Separator, + Shortcut, + Loading, + // + Root as Command, + Dialog as CommandDialog, + Empty as CommandEmpty, + Group as CommandGroup, + Item as CommandItem, + LinkItem as CommandLinkItem, + Input as CommandInput, + List as CommandList, + Separator as CommandSeparator, + Shortcut as CommandShortcut, + Loading as CommandLoading +}; diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-checkbox-item.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-checkbox-item.svelte new file mode 100644 index 0000000..3e0bede --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-checkbox-item.svelte @@ -0,0 +1,38 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.()} + {/snippet} + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-content.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-content.svelte new file mode 100644 index 0000000..7992986 --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-content.svelte @@ -0,0 +1,25 @@ + + + + + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-group-heading.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-group-heading.svelte new file mode 100644 index 0000000..44f2034 --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-group-heading.svelte @@ -0,0 +1,21 @@ + + + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-group.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-group.svelte new file mode 100644 index 0000000..9dab5fa --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-group.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-item.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-item.svelte new file mode 100644 index 0000000..7621bfc --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-item.svelte @@ -0,0 +1,27 @@ + + + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-label.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-label.svelte new file mode 100644 index 0000000..0d15c64 --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-label.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-radio-group.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-radio-group.svelte new file mode 100644 index 0000000..70de32f --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-radio-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-radio-item.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-radio-item.svelte new file mode 100644 index 0000000..e42e16c --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-radio-item.svelte @@ -0,0 +1,31 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.({ checked })} + {/snippet} + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-separator.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-separator.svelte new file mode 100644 index 0000000..eb69cc5 --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-shortcut.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-shortcut.svelte new file mode 100644 index 0000000..bc2bf37 --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-sub-content.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-sub-content.svelte new file mode 100644 index 0000000..6230fa4 --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-sub-content.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte new file mode 100644 index 0000000..c382493 --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/frontend/src/lib/components/ui/context-menu/context-menu-trigger.svelte b/frontend/src/lib/components/ui/context-menu/context-menu-trigger.svelte new file mode 100644 index 0000000..74b9e56 --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/context-menu-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/context-menu/index.ts b/frontend/src/lib/components/ui/context-menu/index.ts new file mode 100644 index 0000000..a0a072b --- /dev/null +++ b/frontend/src/lib/components/ui/context-menu/index.ts @@ -0,0 +1,51 @@ +import { ContextMenu as ContextMenuPrimitive } from 'bits-ui'; + +import Trigger from './context-menu-trigger.svelte'; +import Group from './context-menu-group.svelte'; +import RadioGroup from './context-menu-radio-group.svelte'; +import Item from './context-menu-item.svelte'; +import GroupHeading from './context-menu-group-heading.svelte'; +import Content from './context-menu-content.svelte'; +import Shortcut from './context-menu-shortcut.svelte'; +import RadioItem from './context-menu-radio-item.svelte'; +import Separator from './context-menu-separator.svelte'; +import SubContent from './context-menu-sub-content.svelte'; +import SubTrigger from './context-menu-sub-trigger.svelte'; +import CheckboxItem from './context-menu-checkbox-item.svelte'; +import Label from './context-menu-label.svelte'; +const Sub = ContextMenuPrimitive.Sub; +const Root = ContextMenuPrimitive.Root; + +export { + Sub, + Root, + Item, + GroupHeading, + Label, + Group, + Trigger, + Content, + Shortcut, + Separator, + RadioItem, + SubContent, + SubTrigger, + RadioGroup, + CheckboxItem, + // + Root as ContextMenu, + Sub as ContextMenuSub, + Item as ContextMenuItem, + GroupHeading as ContextMenuGroupHeading, + Group as ContextMenuGroup, + Content as ContextMenuContent, + Trigger as ContextMenuTrigger, + Shortcut as ContextMenuShortcut, + RadioItem as ContextMenuRadioItem, + Separator as ContextMenuSeparator, + RadioGroup as ContextMenuRadioGroup, + SubContent as ContextMenuSubContent, + SubTrigger as ContextMenuSubTrigger, + CheckboxItem as ContextMenuCheckboxItem, + Label as ContextMenuLabel +}; diff --git a/frontend/src/lib/components/ui/data-table/data-table.svelte.ts b/frontend/src/lib/components/ui/data-table/data-table.svelte.ts new file mode 100644 index 0000000..b7882eb --- /dev/null +++ b/frontend/src/lib/components/ui/data-table/data-table.svelte.ts @@ -0,0 +1,141 @@ +import { + type RowData, + type TableOptions, + type TableOptionsResolved, + type TableState, + createTable +} from '@tanstack/table-core'; + +/** + * Creates a reactive TanStack table object for Svelte. + * @param options Table options to create the table with. + * @returns A reactive table object. + * @example + * ```svelte + * + * + * + * + * {#each table.getHeaderGroups() as headerGroup} + * + * {#each headerGroup.headers as header} + * + * {/each} + * + * {/each} + * + * + *
    + * + *
    + * ``` + */ +export function createSvelteTable(options: TableOptions) { + const resolvedOptions: TableOptionsResolved = mergeObjects( + { + state: {}, + onStateChange() {}, + renderFallbackValue: null, + mergeOptions: ( + defaultOptions: TableOptions, + options: Partial> + ) => { + return mergeObjects(defaultOptions, options); + } + }, + options + ); + + const table = createTable(resolvedOptions); + let state = $state>(table.initialState); + + function updateOptions() { + table.setOptions((prev) => { + return mergeObjects(prev, options, { + state: mergeObjects(state, options.state || {}), + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onStateChange: (updater: any) => { + if (updater instanceof Function) state = updater(state); + else state = mergeObjects(state, updater); + + options.onStateChange?.(updater); + } + }); + }); + } + + updateOptions(); + + $effect.pre(() => { + updateOptions(); + }); + + return table; +} + +type MaybeThunk = T | (() => T | null | undefined); +type Intersection = (T extends [infer H, ...infer R] + ? H & Intersection + : unknown) & {}; + +/** + * Lazily merges several objects (or thunks) while preserving + * getter semantics from every source. + * + * Proxy-based to avoid known WebKit recursion issue. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function mergeObjects[]>( + ...sources: Sources +): Intersection<{ [K in keyof Sources]: Sources[K] }> { + const resolve = (src: MaybeThunk): T | undefined => + typeof src === 'function' ? (src() ?? undefined) : src; + + const findSourceWithKey = (key: PropertyKey) => { + for (let i = sources.length - 1; i >= 0; i--) { + const obj = resolve(sources[i]); + if (obj && key in obj) return obj; + } + return undefined; + }; + + return new Proxy(Object.create(null), { + get(_, key) { + const src = findSourceWithKey(key); + + return src?.[key as never]; + }, + + has(_, key) { + return !!findSourceWithKey(key); + }, + + ownKeys(): (string | symbol)[] { + const all = new Set(); + for (const s of sources) { + const obj = resolve(s); + if (obj) { + for (const k of Reflect.ownKeys(obj) as (string | symbol)[]) { + all.add(k); + } + } + } + return [...all]; + }, + + getOwnPropertyDescriptor(_, key) { + const src = findSourceWithKey(key); + if (!src) return undefined; + return { + configurable: true, + enumerable: true, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: (src as any)[key], + writable: true + }; + } + }) as Intersection<{ [K in keyof Sources]: Sources[K] }>; +} diff --git a/frontend/src/lib/components/ui/data-table/flex-render.svelte b/frontend/src/lib/components/ui/data-table/flex-render.svelte new file mode 100644 index 0000000..d052a12 --- /dev/null +++ b/frontend/src/lib/components/ui/data-table/flex-render.svelte @@ -0,0 +1,36 @@ + + +{#if typeof content === 'string'} + {content} +{:else if content instanceof Function} + + + {@const result = content(context as any)} + {#if result instanceof RenderComponentConfig} + {@const { component: Component, props } = result} + + {:else if result instanceof RenderSnippetConfig} + {@const { snippet, params } = result} + {@render snippet(params)} + {:else} + {result} + {/if} +{/if} diff --git a/frontend/src/lib/components/ui/data-table/index.ts b/frontend/src/lib/components/ui/data-table/index.ts new file mode 100644 index 0000000..05fb821 --- /dev/null +++ b/frontend/src/lib/components/ui/data-table/index.ts @@ -0,0 +1,3 @@ +export { default as FlexRender } from './flex-render.svelte'; +export { renderComponent, renderSnippet } from './render-helpers.js'; +export { createSvelteTable } from './data-table.svelte.js'; diff --git a/frontend/src/lib/components/ui/data-table/render-helpers.ts b/frontend/src/lib/components/ui/data-table/render-helpers.ts new file mode 100644 index 0000000..01694fe --- /dev/null +++ b/frontend/src/lib/components/ui/data-table/render-helpers.ts @@ -0,0 +1,111 @@ +import type { Component, ComponentProps, Snippet } from 'svelte'; + +/** + * A helper class to make it easy to identify Svelte components in + * `columnDef.cell` and `columnDef.header` properties. + * + * > NOTE: This class should only be used internally by the adapter. If you're + * reading this and you don't know what this is for, you probably don't need it. + * + * @example + * ```svelte + * {@const result = content(context as any)} + * {#if result instanceof RenderComponentConfig} + * {@const { component: Component, props } = result} + * + * {/if} + * ``` + */ +export class RenderComponentConfig { + component: TComponent; + props: ComponentProps | Record; + constructor( + component: TComponent, + props: ComponentProps | Record = {} + ) { + this.component = component; + this.props = props; + } +} + +/** + * A helper class to make it easy to identify Svelte Snippets in `columnDef.cell` and `columnDef.header` properties. + * + * > NOTE: This class should only be used internally by the adapter. If you're + * reading this and you don't know what this is for, you probably don't need it. + * + * @example + * ```svelte + * {@const result = content(context as any)} + * {#if result instanceof RenderSnippetConfig} + * {@const { snippet, params } = result} + * {@render snippet(params)} + * {/if} + * ``` + */ +export class RenderSnippetConfig { + snippet: Snippet<[TProps]>; + params: TProps; + constructor(snippet: Snippet<[TProps]>, params: TProps) { + this.snippet = snippet; + this.params = params; + } +} + +/** + * A helper function to help create cells from Svelte components through ColumnDef's `cell` and `header` properties. + * + * This is only to be used with Svelte Components - use `renderSnippet` for Svelte Snippets. + * + * @param component A Svelte component + * @param props The props to pass to `component` + * @returns A `RenderComponentConfig` object that helps svelte-table know how to render the header/cell component. + * @example + * ```ts + * // +page.svelte + * const defaultColumns = [ + * columnHelper.accessor('name', { + * header: header => renderComponent(SortHeader, { label: 'Name', header }), + * }), + * columnHelper.accessor('state', { + * header: header => renderComponent(SortHeader, { label: 'State', header }), + * }), + * ] + * ``` + * @see {@link https://tanstack.com/table/latest/docs/guide/column-defs} + */ +export function renderComponent< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + T extends Component, + Props extends ComponentProps +>(component: T, props: Props = {} as Props) { + return new RenderComponentConfig(component, props); +} + +/** + * A helper function to help create cells from Svelte Snippets through ColumnDef's `cell` and `header` properties. + * + * The snippet must only take one parameter. + * + * This is only to be used with Snippets - use `renderComponent` for Svelte Components. + * + * @param snippet + * @param params + * @returns - A `RenderSnippetConfig` object that helps svelte-table know how to render the header/cell snippet. + * @example + * ```ts + * // +page.svelte + * const defaultColumns = [ + * columnHelper.accessor('name', { + * cell: cell => renderSnippet(nameSnippet, { name: cell.row.name }), + * }), + * columnHelper.accessor('state', { + * cell: cell => renderSnippet(stateSnippet, { state: cell.row.state }), + * }), + * ] + * ``` + * @see {@link https://tanstack.com/table/latest/docs/guide/column-defs} + */ +export function renderSnippet(snippet: Snippet<[TProps]>, params: TProps = {} as TProps) { + return new RenderSnippetConfig(snippet, params); +} diff --git a/frontend/src/lib/components/ui/dialog/dialog-close.svelte b/frontend/src/lib/components/ui/dialog/dialog-close.svelte new file mode 100644 index 0000000..dc66858 --- /dev/null +++ b/frontend/src/lib/components/ui/dialog/dialog-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/dialog/dialog-content.svelte b/frontend/src/lib/components/ui/dialog/dialog-content.svelte new file mode 100644 index 0000000..ae1c09f --- /dev/null +++ b/frontend/src/lib/components/ui/dialog/dialog-content.svelte @@ -0,0 +1,43 @@ + + + + + + {@render children?.()} + {#if showCloseButton} + + + Close + + {/if} + + diff --git a/frontend/src/lib/components/ui/dialog/dialog-description.svelte b/frontend/src/lib/components/ui/dialog/dialog-description.svelte new file mode 100644 index 0000000..aceeb05 --- /dev/null +++ b/frontend/src/lib/components/ui/dialog/dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/dialog/dialog-footer.svelte b/frontend/src/lib/components/ui/dialog/dialog-footer.svelte new file mode 100644 index 0000000..efca501 --- /dev/null +++ b/frontend/src/lib/components/ui/dialog/dialog-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/dialog/dialog-header.svelte b/frontend/src/lib/components/ui/dialog/dialog-header.svelte new file mode 100644 index 0000000..634ecce --- /dev/null +++ b/frontend/src/lib/components/ui/dialog/dialog-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte b/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte new file mode 100644 index 0000000..603307f --- /dev/null +++ b/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/dialog/dialog-title.svelte b/frontend/src/lib/components/ui/dialog/dialog-title.svelte new file mode 100644 index 0000000..a8aab9b --- /dev/null +++ b/frontend/src/lib/components/ui/dialog/dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte b/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte new file mode 100644 index 0000000..65e1197 --- /dev/null +++ b/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/dialog/index.ts b/frontend/src/lib/components/ui/dialog/index.ts new file mode 100644 index 0000000..e15d8dc --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/drawer/drawer-close.svelte b/frontend/src/lib/components/ui/drawer/drawer-close.svelte new file mode 100644 index 0000000..eb1c20a --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/drawer-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/drawer/drawer-content.svelte b/frontend/src/lib/components/ui/drawer/drawer-content.svelte new file mode 100644 index 0000000..7a11dbc --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/drawer-content.svelte @@ -0,0 +1,37 @@ + + + + + + + {@render children?.()} + + diff --git a/frontend/src/lib/components/ui/drawer/drawer-description.svelte b/frontend/src/lib/components/ui/drawer/drawer-description.svelte new file mode 100644 index 0000000..81b2b00 --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/drawer-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/drawer/drawer-footer.svelte b/frontend/src/lib/components/ui/drawer/drawer-footer.svelte new file mode 100644 index 0000000..050316d --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/drawer-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/drawer/drawer-header.svelte b/frontend/src/lib/components/ui/drawer/drawer-header.svelte new file mode 100644 index 0000000..2e5284a --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/drawer-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/drawer/drawer-nested.svelte b/frontend/src/lib/components/ui/drawer/drawer-nested.svelte new file mode 100644 index 0000000..3115c72 --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/drawer-nested.svelte @@ -0,0 +1,12 @@ + + + diff --git a/frontend/src/lib/components/ui/drawer/drawer-overlay.svelte b/frontend/src/lib/components/ui/drawer/drawer-overlay.svelte new file mode 100644 index 0000000..a92f925 --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/drawer-overlay.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/drawer/drawer-title.svelte b/frontend/src/lib/components/ui/drawer/drawer-title.svelte new file mode 100644 index 0000000..3a19341 --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/drawer-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/drawer/drawer-trigger.svelte b/frontend/src/lib/components/ui/drawer/drawer-trigger.svelte new file mode 100644 index 0000000..3ec875e --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/drawer-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/drawer/drawer.svelte b/frontend/src/lib/components/ui/drawer/drawer.svelte new file mode 100644 index 0000000..2597799 --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/drawer.svelte @@ -0,0 +1,12 @@ + + + diff --git a/frontend/src/lib/components/ui/drawer/index.ts b/frontend/src/lib/components/ui/drawer/index.ts new file mode 100644 index 0000000..9f1078b --- /dev/null +++ b/frontend/src/lib/components/ui/drawer/index.ts @@ -0,0 +1,41 @@ +import { Drawer as DrawerPrimitive } from 'vaul-svelte'; + +import Root from './drawer.svelte'; +import Content from './drawer-content.svelte'; +import Description from './drawer-description.svelte'; +import Overlay from './drawer-overlay.svelte'; +import Footer from './drawer-footer.svelte'; +import Header from './drawer-header.svelte'; +import Title from './drawer-title.svelte'; +import NestedRoot from './drawer-nested.svelte'; +import Close from './drawer-close.svelte'; +import Trigger from './drawer-trigger.svelte'; + +const Portal: typeof DrawerPrimitive.Portal = DrawerPrimitive.Portal; + +export { + Root, + NestedRoot, + Content, + Description, + Overlay, + Footer, + Header, + Title, + Trigger, + Portal, + Close, + + // + Root as Drawer, + NestedRoot as DrawerNestedRoot, + Content as DrawerContent, + Description as DrawerDescription, + Overlay as DrawerOverlay, + Footer as DrawerFooter, + Header as DrawerHeader, + Title as DrawerTitle, + Trigger as DrawerTrigger, + Portal as DrawerPortal, + Close as DrawerClose +}; diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte new file mode 100644 index 0000000..b5fec96 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte new file mode 100644 index 0000000..5798e05 --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte @@ -0,0 +1,27 @@ + + + + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte new file mode 100644 index 0000000..77ee2d2 --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte new file mode 100644 index 0000000..91bc28e --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte new file mode 100644 index 0000000..3366d64 --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte @@ -0,0 +1,27 @@ + + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte new file mode 100644 index 0000000..b5875c0 --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte new file mode 100644 index 0000000..fe0a159 --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte new file mode 100644 index 0000000..755d85c --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte new file mode 100644 index 0000000..6bf5259 --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte new file mode 100644 index 0000000..604e7dc --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte new file mode 100644 index 0000000..782a9ae --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte new file mode 100644 index 0000000..dbf109f --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte new file mode 100644 index 0000000..92d5a1a --- /dev/null +++ b/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/dropdown-menu/index.ts b/frontend/src/lib/components/ui/dropdown-menu/index.ts new file mode 100644 index 0000000..6bd10fa --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/form/form-button.svelte b/frontend/src/lib/components/ui/form/form-button.svelte new file mode 100644 index 0000000..95245bf --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-button.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/form/form-description.svelte b/frontend/src/lib/components/ui/form/form-description.svelte new file mode 100644 index 0000000..dc84f1f --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/form/form-element-field.svelte b/frontend/src/lib/components/ui/form/form-element-field.svelte new file mode 100644 index 0000000..c0ef5f7 --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-element-field.svelte @@ -0,0 +1,24 @@ + + + + {#snippet children({ constraints, errors, tainted, value })} +
    + {@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })} +
    + {/snippet} +
    diff --git a/frontend/src/lib/components/ui/form/form-field-errors.svelte b/frontend/src/lib/components/ui/form/form-field-errors.svelte new file mode 100644 index 0000000..972e074 --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-field-errors.svelte @@ -0,0 +1,30 @@ + + + + {#snippet children({ errors, errorProps })} + {#if childrenProp} + {@render childrenProp({ errors, errorProps })} + {:else} + {#each errors as error (error)} +
    {error}
    + {/each} + {/if} + {/snippet} +
    diff --git a/frontend/src/lib/components/ui/form/form-field.svelte b/frontend/src/lib/components/ui/form/form-field.svelte new file mode 100644 index 0000000..f399309 --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-field.svelte @@ -0,0 +1,29 @@ + + + + {#snippet children({ constraints, errors, tainted, value })} +
    + {@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })} +
    + {/snippet} +
    diff --git a/frontend/src/lib/components/ui/form/form-fieldset.svelte b/frontend/src/lib/components/ui/form/form-fieldset.svelte new file mode 100644 index 0000000..154f8e7 --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-fieldset.svelte @@ -0,0 +1,15 @@ + + + diff --git a/frontend/src/lib/components/ui/form/form-label.svelte b/frontend/src/lib/components/ui/form/form-label.svelte new file mode 100644 index 0000000..60f2623 --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-label.svelte @@ -0,0 +1,24 @@ + + + + {#snippet child({ props })} + + {/snippet} + diff --git a/frontend/src/lib/components/ui/form/form-legend.svelte b/frontend/src/lib/components/ui/form/form-legend.svelte new file mode 100644 index 0000000..9ecb01a --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-legend.svelte @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/lib/components/ui/form/index.ts b/frontend/src/lib/components/ui/form/index.ts new file mode 100644 index 0000000..d5cbc77 --- /dev/null +++ b/frontend/src/lib/components/ui/form/index.ts @@ -0,0 +1,33 @@ +import * as FormPrimitive from 'formsnap'; +import Description from './form-description.svelte'; +import Label from './form-label.svelte'; +import FieldErrors from './form-field-errors.svelte'; +import Field from './form-field.svelte'; +import Fieldset from './form-fieldset.svelte'; +import Legend from './form-legend.svelte'; +import ElementField from './form-element-field.svelte'; +import Button from './form-button.svelte'; + +const Control = FormPrimitive.Control; + +export { + Field, + Control, + Label, + Button, + FieldErrors, + Description, + Fieldset, + Legend, + ElementField, + // + Field as FormField, + Control as FormControl, + Description as FormDescription, + Label as FormLabel, + FieldErrors as FormFieldErrors, + Fieldset as FormFieldset, + Legend as FormLegend, + ElementField as FormElementField, + Button as FormButton +}; diff --git a/frontend/src/lib/components/ui/hover-card/hover-card-content.svelte b/frontend/src/lib/components/ui/hover-card/hover-card-content.svelte new file mode 100644 index 0000000..5eb795f --- /dev/null +++ b/frontend/src/lib/components/ui/hover-card/hover-card-content.svelte @@ -0,0 +1,29 @@ + + + + + diff --git a/frontend/src/lib/components/ui/hover-card/hover-card-trigger.svelte b/frontend/src/lib/components/ui/hover-card/hover-card-trigger.svelte new file mode 100644 index 0000000..52cb287 --- /dev/null +++ b/frontend/src/lib/components/ui/hover-card/hover-card-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/hover-card/index.ts b/frontend/src/lib/components/ui/hover-card/index.ts new file mode 100644 index 0000000..1a24198 --- /dev/null +++ b/frontend/src/lib/components/ui/hover-card/index.ts @@ -0,0 +1,14 @@ +import { LinkPreview as HoverCardPrimitive } from 'bits-ui'; +import Content from './hover-card-content.svelte'; +import Trigger from './hover-card-trigger.svelte'; + +const Root = HoverCardPrimitive.Root; + +export { + Root, + Content, + Trigger, + Root as HoverCard, + Content as HoverCardContent, + Trigger as HoverCardTrigger +}; diff --git a/frontend/src/lib/components/ui/input-otp/index.ts b/frontend/src/lib/components/ui/input-otp/index.ts new file mode 100644 index 0000000..b86db72 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/input-otp/input-otp-group.svelte b/frontend/src/lib/components/ui/input-otp/input-otp-group.svelte new file mode 100644 index 0000000..5df966a --- /dev/null +++ b/frontend/src/lib/components/ui/input-otp/input-otp-group.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/input-otp/input-otp-separator.svelte b/frontend/src/lib/components/ui/input-otp/input-otp-separator.svelte new file mode 100644 index 0000000..4064038 --- /dev/null +++ b/frontend/src/lib/components/ui/input-otp/input-otp-separator.svelte @@ -0,0 +1,19 @@ + + +
    + {#if children} + {@render children?.()} + {:else} + + {/if} +
    diff --git a/frontend/src/lib/components/ui/input-otp/input-otp-slot.svelte b/frontend/src/lib/components/ui/input-otp/input-otp-slot.svelte new file mode 100644 index 0000000..f7884b1 --- /dev/null +++ b/frontend/src/lib/components/ui/input-otp/input-otp-slot.svelte @@ -0,0 +1,31 @@ + + + + {cell.char} + {#if cell.hasFakeCaret} +
    + +
    + {/if} +
    diff --git a/frontend/src/lib/components/ui/input-otp/input-otp.svelte b/frontend/src/lib/components/ui/input-otp/input-otp.svelte new file mode 100644 index 0000000..f805f63 --- /dev/null +++ b/frontend/src/lib/components/ui/input-otp/input-otp.svelte @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/lib/components/ui/input/index.ts b/frontend/src/lib/components/ui/input/index.ts new file mode 100644 index 0000000..14e47ac --- /dev/null +++ b/frontend/src/lib/components/ui/input/index.ts @@ -0,0 +1,7 @@ +import Root from './input.svelte'; + +export { + Root, + // + Root as Input +}; diff --git a/frontend/src/lib/components/ui/input/input.svelte b/frontend/src/lib/components/ui/input/input.svelte new file mode 100644 index 0000000..22340fa --- /dev/null +++ b/frontend/src/lib/components/ui/input/input.svelte @@ -0,0 +1,51 @@ + + +{#if type === 'file'} + +{:else} + +{/if} diff --git a/frontend/src/lib/components/ui/label/index.ts b/frontend/src/lib/components/ui/label/index.ts new file mode 100644 index 0000000..7518013 --- /dev/null +++ b/frontend/src/lib/components/ui/label/index.ts @@ -0,0 +1,7 @@ +import Root from './label.svelte'; + +export { + Root, + // + Root as Label +}; diff --git a/frontend/src/lib/components/ui/label/label.svelte b/frontend/src/lib/components/ui/label/label.svelte new file mode 100644 index 0000000..5099a8f --- /dev/null +++ b/frontend/src/lib/components/ui/label/label.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/menubar/index.ts b/frontend/src/lib/components/ui/menubar/index.ts new file mode 100644 index 0000000..ed5acec --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/index.ts @@ -0,0 +1,54 @@ +import { Menubar as MenubarPrimitive } from 'bits-ui'; +import Root from './menubar.svelte'; +import CheckboxItem from './menubar-checkbox-item.svelte'; +import Content from './menubar-content.svelte'; +import Item from './menubar-item.svelte'; +import Group from './menubar-group.svelte'; +import RadioItem from './menubar-radio-item.svelte'; +import Separator from './menubar-separator.svelte'; +import Shortcut from './menubar-shortcut.svelte'; +import SubContent from './menubar-sub-content.svelte'; +import SubTrigger from './menubar-sub-trigger.svelte'; +import Trigger from './menubar-trigger.svelte'; +import Label from './menubar-label.svelte'; +import GroupHeading from './menubar-group-heading.svelte'; + +const Menu = MenubarPrimitive.Menu; +const Sub = MenubarPrimitive.Sub; +const RadioGroup = MenubarPrimitive.RadioGroup; + +export { + Root, + CheckboxItem, + Content, + Item, + RadioItem, + Separator, + Shortcut, + SubContent, + SubTrigger, + Trigger, + Menu, + Group, + Sub, + RadioGroup, + Label, + GroupHeading, + // + Root as Menubar, + CheckboxItem as MenubarCheckboxItem, + Content as MenubarContent, + Item as MenubarItem, + RadioItem as MenubarRadioItem, + Separator as MenubarSeparator, + Shortcut as MenubarShortcut, + SubContent as MenubarSubContent, + SubTrigger as MenubarSubTrigger, + Trigger as MenubarTrigger, + Menu as MenubarMenu, + Group as MenubarGroup, + Sub as MenubarSub, + RadioGroup as MenubarRadioGroup, + Label as MenubarLabel, + GroupHeading as MenubarGroupHeading +}; diff --git a/frontend/src/lib/components/ui/menubar/menubar-checkbox-item.svelte b/frontend/src/lib/components/ui/menubar/menubar-checkbox-item.svelte new file mode 100644 index 0000000..9e7f71b --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-checkbox-item.svelte @@ -0,0 +1,41 @@ + + + + {#snippet children({ checked, indeterminate })} + + {#if indeterminate} + + {:else} + + {/if} + + {@render childrenProp?.()} + {/snippet} + diff --git a/frontend/src/lib/components/ui/menubar/menubar-content.svelte b/frontend/src/lib/components/ui/menubar/menubar-content.svelte new file mode 100644 index 0000000..d71986b --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-content.svelte @@ -0,0 +1,33 @@ + + + + + diff --git a/frontend/src/lib/components/ui/menubar/menubar-group-heading.svelte b/frontend/src/lib/components/ui/menubar/menubar-group-heading.svelte new file mode 100644 index 0000000..460a007 --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-group-heading.svelte @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/lib/components/ui/menubar/menubar-group.svelte b/frontend/src/lib/components/ui/menubar/menubar-group.svelte new file mode 100644 index 0000000..edb66fe --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-group.svelte @@ -0,0 +1,12 @@ + + + diff --git a/frontend/src/lib/components/ui/menubar/menubar-item.svelte b/frontend/src/lib/components/ui/menubar/menubar-item.svelte new file mode 100644 index 0000000..81b3b47 --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-item.svelte @@ -0,0 +1,27 @@ + + + diff --git a/frontend/src/lib/components/ui/menubar/menubar-label.svelte b/frontend/src/lib/components/ui/menubar/menubar-label.svelte new file mode 100644 index 0000000..a9474df --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-label.svelte @@ -0,0 +1,25 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/menubar/menubar-radio-item.svelte b/frontend/src/lib/components/ui/menubar/menubar-radio-item.svelte new file mode 100644 index 0000000..4864c34 --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-radio-item.svelte @@ -0,0 +1,31 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.({ checked })} + {/snippet} + diff --git a/frontend/src/lib/components/ui/menubar/menubar-separator.svelte b/frontend/src/lib/components/ui/menubar/menubar-separator.svelte new file mode 100644 index 0000000..4d340f4 --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/menubar/menubar-shortcut.svelte b/frontend/src/lib/components/ui/menubar/menubar-shortcut.svelte new file mode 100644 index 0000000..11b3b1d --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/menubar/menubar-sub-content.svelte b/frontend/src/lib/components/ui/menubar/menubar-sub-content.svelte new file mode 100644 index 0000000..5b934c5 --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-sub-content.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/menubar/menubar-sub-trigger.svelte b/frontend/src/lib/components/ui/menubar/menubar-sub-trigger.svelte new file mode 100644 index 0000000..51f5cef --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-sub-trigger.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/frontend/src/lib/components/ui/menubar/menubar-trigger.svelte b/frontend/src/lib/components/ui/menubar/menubar-trigger.svelte new file mode 100644 index 0000000..3b374b7 --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar-trigger.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/menubar/menubar.svelte b/frontend/src/lib/components/ui/menubar/menubar.svelte new file mode 100644 index 0000000..136addb --- /dev/null +++ b/frontend/src/lib/components/ui/menubar/menubar.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/navigation-menu/index.ts b/frontend/src/lib/components/ui/navigation-menu/index.ts new file mode 100644 index 0000000..189ce9e --- /dev/null +++ b/frontend/src/lib/components/ui/navigation-menu/index.ts @@ -0,0 +1,28 @@ +import Root from './navigation-menu.svelte'; +import Content from './navigation-menu-content.svelte'; +import Indicator from './navigation-menu-indicator.svelte'; +import Item from './navigation-menu-item.svelte'; +import Link from './navigation-menu-link.svelte'; +import List from './navigation-menu-list.svelte'; +import Trigger from './navigation-menu-trigger.svelte'; +import Viewport from './navigation-menu-viewport.svelte'; + +export { + Root, + Content, + Indicator, + Item, + Link, + List, + Trigger, + Viewport, + // + Root as NavigationMenuRoot, + Content as NavigationMenuContent, + Indicator as NavigationMenuIndicator, + Item as NavigationMenuItem, + Link as NavigationMenuLink, + List as NavigationMenuList, + Trigger as NavigationMenuTrigger, + Viewport as NavigationMenuViewport +}; diff --git a/frontend/src/lib/components/ui/navigation-menu/navigation-menu-content.svelte b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-content.svelte new file mode 100644 index 0000000..fb40c95 --- /dev/null +++ b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-content.svelte @@ -0,0 +1,21 @@ + + + diff --git a/frontend/src/lib/components/ui/navigation-menu/navigation-menu-indicator.svelte b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-indicator.svelte new file mode 100644 index 0000000..8afe57a --- /dev/null +++ b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-indicator.svelte @@ -0,0 +1,22 @@ + + + +
    +
    diff --git a/frontend/src/lib/components/ui/navigation-menu/navigation-menu-item.svelte b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-item.svelte new file mode 100644 index 0000000..563670d --- /dev/null +++ b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-item.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/navigation-menu/navigation-menu-link.svelte b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-link.svelte new file mode 100644 index 0000000..05a2b7d --- /dev/null +++ b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-link.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/navigation-menu/navigation-menu-list.svelte b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-list.svelte new file mode 100644 index 0000000..7d7bb0f --- /dev/null +++ b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-list.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/navigation-menu/navigation-menu-trigger.svelte b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-trigger.svelte new file mode 100644 index 0000000..2256441 --- /dev/null +++ b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-trigger.svelte @@ -0,0 +1,34 @@ + + + + + + {@render children?.()} + + diff --git a/frontend/src/lib/components/ui/navigation-menu/navigation-menu-viewport.svelte b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-viewport.svelte new file mode 100644 index 0000000..ba20f66 --- /dev/null +++ b/frontend/src/lib/components/ui/navigation-menu/navigation-menu-viewport.svelte @@ -0,0 +1,22 @@ + + +
    + +
    diff --git a/frontend/src/lib/components/ui/navigation-menu/navigation-menu.svelte b/frontend/src/lib/components/ui/navigation-menu/navigation-menu.svelte new file mode 100644 index 0000000..2f81ffe --- /dev/null +++ b/frontend/src/lib/components/ui/navigation-menu/navigation-menu.svelte @@ -0,0 +1,32 @@ + + + + {@render children?.()} + + {#if viewport} + + {/if} + diff --git a/frontend/src/lib/components/ui/pagination/index.ts b/frontend/src/lib/components/ui/pagination/index.ts new file mode 100644 index 0000000..e30c896 --- /dev/null +++ b/frontend/src/lib/components/ui/pagination/index.ts @@ -0,0 +1,25 @@ +import Root from './pagination.svelte'; +import Content from './pagination-content.svelte'; +import Item from './pagination-item.svelte'; +import Link from './pagination-link.svelte'; +import PrevButton from './pagination-prev-button.svelte'; +import NextButton from './pagination-next-button.svelte'; +import Ellipsis from './pagination-ellipsis.svelte'; + +export { + Root, + Content, + Item, + Link, + PrevButton, + NextButton, + Ellipsis, + // + Root as Pagination, + Content as PaginationContent, + Item as PaginationItem, + Link as PaginationLink, + PrevButton as PaginationPrevButton, + NextButton as PaginationNextButton, + Ellipsis as PaginationEllipsis +}; diff --git a/frontend/src/lib/components/ui/pagination/pagination-content.svelte b/frontend/src/lib/components/ui/pagination/pagination-content.svelte new file mode 100644 index 0000000..1ea6ed2 --- /dev/null +++ b/frontend/src/lib/components/ui/pagination/pagination-content.svelte @@ -0,0 +1,20 @@ + + +
      + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/pagination/pagination-ellipsis.svelte b/frontend/src/lib/components/ui/pagination/pagination-ellipsis.svelte new file mode 100644 index 0000000..4d4c9de --- /dev/null +++ b/frontend/src/lib/components/ui/pagination/pagination-ellipsis.svelte @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/lib/components/ui/pagination/pagination-item.svelte b/frontend/src/lib/components/ui/pagination/pagination-item.svelte new file mode 100644 index 0000000..a046806 --- /dev/null +++ b/frontend/src/lib/components/ui/pagination/pagination-item.svelte @@ -0,0 +1,14 @@ + + +
  • + {@render children?.()} +
  • diff --git a/frontend/src/lib/components/ui/pagination/pagination-link.svelte b/frontend/src/lib/components/ui/pagination/pagination-link.svelte new file mode 100644 index 0000000..ddfd4c5 --- /dev/null +++ b/frontend/src/lib/components/ui/pagination/pagination-link.svelte @@ -0,0 +1,39 @@ + + +{#snippet Fallback()} + {page.value} +{/snippet} + + diff --git a/frontend/src/lib/components/ui/pagination/pagination-next-button.svelte b/frontend/src/lib/components/ui/pagination/pagination-next-button.svelte new file mode 100644 index 0000000..5db16fe --- /dev/null +++ b/frontend/src/lib/components/ui/pagination/pagination-next-button.svelte @@ -0,0 +1,33 @@ + + +{#snippet Fallback()} + Next + +{/snippet} + + diff --git a/frontend/src/lib/components/ui/pagination/pagination-prev-button.svelte b/frontend/src/lib/components/ui/pagination/pagination-prev-button.svelte new file mode 100644 index 0000000..e306ed0 --- /dev/null +++ b/frontend/src/lib/components/ui/pagination/pagination-prev-button.svelte @@ -0,0 +1,33 @@ + + +{#snippet Fallback()} + + Previous +{/snippet} + + diff --git a/frontend/src/lib/components/ui/pagination/pagination.svelte b/frontend/src/lib/components/ui/pagination/pagination.svelte new file mode 100644 index 0000000..a028545 --- /dev/null +++ b/frontend/src/lib/components/ui/pagination/pagination.svelte @@ -0,0 +1,28 @@ + + + diff --git a/frontend/src/lib/components/ui/popover/index.ts b/frontend/src/lib/components/ui/popover/index.ts new file mode 100644 index 0000000..e3bb9cb --- /dev/null +++ b/frontend/src/lib/components/ui/popover/index.ts @@ -0,0 +1,17 @@ +import { Popover as PopoverPrimitive } from 'bits-ui'; +import Content from './popover-content.svelte'; +import Trigger from './popover-trigger.svelte'; +const Root = PopoverPrimitive.Root; +const Close = PopoverPrimitive.Close; + +export { + Root, + Content, + Trigger, + Close, + // + Root as Popover, + Content as PopoverContent, + Trigger as PopoverTrigger, + Close as PopoverClose +}; diff --git a/frontend/src/lib/components/ui/popover/popover-content.svelte b/frontend/src/lib/components/ui/popover/popover-content.svelte new file mode 100644 index 0000000..3d5567a --- /dev/null +++ b/frontend/src/lib/components/ui/popover/popover-content.svelte @@ -0,0 +1,29 @@ + + + + + diff --git a/frontend/src/lib/components/ui/popover/popover-trigger.svelte b/frontend/src/lib/components/ui/popover/popover-trigger.svelte new file mode 100644 index 0000000..b19d262 --- /dev/null +++ b/frontend/src/lib/components/ui/popover/popover-trigger.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/progress/index.ts b/frontend/src/lib/components/ui/progress/index.ts new file mode 100644 index 0000000..12a9c12 --- /dev/null +++ b/frontend/src/lib/components/ui/progress/index.ts @@ -0,0 +1,7 @@ +import Root from './progress.svelte'; + +export { + Root, + // + Root as Progress +}; diff --git a/frontend/src/lib/components/ui/progress/progress.svelte b/frontend/src/lib/components/ui/progress/progress.svelte new file mode 100644 index 0000000..cb65918 --- /dev/null +++ b/frontend/src/lib/components/ui/progress/progress.svelte @@ -0,0 +1,27 @@ + + + +
    +
    diff --git a/frontend/src/lib/components/ui/radio-group/index.ts b/frontend/src/lib/components/ui/radio-group/index.ts new file mode 100644 index 0000000..a614a5e --- /dev/null +++ b/frontend/src/lib/components/ui/radio-group/index.ts @@ -0,0 +1,10 @@ +import Root from './radio-group.svelte'; +import Item from './radio-group-item.svelte'; + +export { + Root, + Item, + // + Root as RadioGroup, + Item as RadioGroupItem +}; diff --git a/frontend/src/lib/components/ui/radio-group/radio-group-item.svelte b/frontend/src/lib/components/ui/radio-group/radio-group-item.svelte new file mode 100644 index 0000000..cf98233 --- /dev/null +++ b/frontend/src/lib/components/ui/radio-group/radio-group-item.svelte @@ -0,0 +1,31 @@ + + + + {#snippet children({ checked })} +
    + {#if checked} + + {/if} +
    + {/snippet} +
    diff --git a/frontend/src/lib/components/ui/radio-group/radio-group.svelte b/frontend/src/lib/components/ui/radio-group/radio-group.svelte new file mode 100644 index 0000000..c43a69f --- /dev/null +++ b/frontend/src/lib/components/ui/radio-group/radio-group.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/range-calendar/index.ts b/frontend/src/lib/components/ui/range-calendar/index.ts new file mode 100644 index 0000000..8de7132 --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/index.ts @@ -0,0 +1,42 @@ +import { RangeCalendar as RangeCalendarPrimitive } from 'bits-ui'; +import Root from './range-calendar.svelte'; +import Cell from './range-calendar-cell.svelte'; +import Day from './range-calendar-day.svelte'; +import Grid from './range-calendar-grid.svelte'; +import Header from './range-calendar-header.svelte'; +import Months from './range-calendar-months.svelte'; +import GridRow from './range-calendar-grid-row.svelte'; +import Heading from './range-calendar-heading.svelte'; +import HeadCell from './range-calendar-head-cell.svelte'; +import NextButton from './range-calendar-next-button.svelte'; +import PrevButton from './range-calendar-prev-button.svelte'; +import MonthSelect from './range-calendar-month-select.svelte'; +import YearSelect from './range-calendar-year-select.svelte'; +import Caption from './range-calendar-caption.svelte'; +import Nav from './range-calendar-nav.svelte'; +import Month from './range-calendar-month.svelte'; + +const GridHead = RangeCalendarPrimitive.GridHead; +const GridBody = RangeCalendarPrimitive.GridBody; + +export { + Day, + Cell, + Grid, + Header, + Months, + GridRow, + Heading, + GridBody, + GridHead, + HeadCell, + NextButton, + PrevButton, + MonthSelect, + YearSelect, + Caption, + Nav, + Month, + // + Root as RangeCalendar +}; diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-caption.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-caption.svelte new file mode 100644 index 0000000..aebfcbe --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-caption.svelte @@ -0,0 +1,76 @@ + + +{#snippet MonthSelect()} + { + if (!placeholder) return; + const v = Number.parseInt(e.currentTarget.value); + const newPlaceholder = placeholder.set({ month: v }); + placeholder = newPlaceholder.subtract({ months: monthIndex }); + }} + /> +{/snippet} + +{#snippet YearSelect()} + +{/snippet} + +{#if captionLayout === 'dropdown'} + {@render MonthSelect()} + {@render YearSelect()} +{:else if captionLayout === 'dropdown-months'} + {@render MonthSelect()} + {#if placeholder} + {formatYear(placeholder)} + {/if} +{:else if captionLayout === 'dropdown-years'} + {#if placeholder} + {formatMonth(placeholder)} + {/if} + {@render YearSelect()} +{:else} + {formatMonth(month)} {formatYear(month)} +{/if} diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-cell.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-cell.svelte new file mode 100644 index 0000000..724bc56 --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-cell.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-day.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-day.svelte new file mode 100644 index 0000000..4d53973 --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-day.svelte @@ -0,0 +1,39 @@ + + +span]:text-xs [&>span]:opacity-70', + className + )} + {...restProps} +/> diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte new file mode 100644 index 0000000..e9da52a --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte @@ -0,0 +1,12 @@ + + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-grid.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-grid.svelte new file mode 100644 index 0000000..145c4e5 --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-grid.svelte @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte new file mode 100644 index 0000000..a0fe70b --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-header.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-header.svelte new file mode 100644 index 0000000..832929e --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-header.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-heading.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-heading.svelte new file mode 100644 index 0000000..1f97fc4 --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-heading.svelte @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-month-select.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-month-select.svelte new file mode 100644 index 0000000..e5c8662 --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-month-select.svelte @@ -0,0 +1,44 @@ + + + + + {#snippet child({ props, monthItems, selectedMonthItem })} + + + {/snippet} + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-month.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-month.svelte new file mode 100644 index 0000000..0fe50c6 --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-month.svelte @@ -0,0 +1,15 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-months.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-months.svelte new file mode 100644 index 0000000..21a914b --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-months.svelte @@ -0,0 +1,19 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-nav.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-nav.svelte new file mode 100644 index 0000000..cd5498f --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-nav.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte new file mode 100644 index 0000000..a53ee6e --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte @@ -0,0 +1,31 @@ + + +{#snippet Fallback()} + +{/snippet} + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte new file mode 100644 index 0000000..625a57c --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte @@ -0,0 +1,31 @@ + + +{#snippet Fallback()} + +{/snippet} + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar-year-select.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar-year-select.svelte new file mode 100644 index 0000000..7c81ceb --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar-year-select.svelte @@ -0,0 +1,43 @@ + + + + + {#snippet child({ props, yearItems, selectedYearItem })} + + + {/snippet} + + diff --git a/frontend/src/lib/components/ui/range-calendar/range-calendar.svelte b/frontend/src/lib/components/ui/range-calendar/range-calendar.svelte new file mode 100644 index 0000000..a811ab2 --- /dev/null +++ b/frontend/src/lib/components/ui/range-calendar/range-calendar.svelte @@ -0,0 +1,112 @@ + + + + {#snippet children({ months, weekdays })} + + + + + + {#each months as month, monthIndex (month)} + + + + + + + + + {#each weekdays as weekday (weekday)} + + {weekday.slice(0, 2)} + + {/each} + + + + {#each month.weeks as weekDates (weekDates)} + + {#each weekDates as date (date)} + + {#if day} + {@render day({ + day: date, + outsideMonth: !isEqualMonth(date, month.value) + })} + {:else} + + {/if} + + {/each} + + {/each} + + + + {/each} + + {/snippet} + diff --git a/frontend/src/lib/components/ui/resizable/index.ts b/frontend/src/lib/components/ui/resizable/index.ts new file mode 100644 index 0000000..49ce88d --- /dev/null +++ b/frontend/src/lib/components/ui/resizable/index.ts @@ -0,0 +1,13 @@ +import { Pane } from 'paneforge'; +import Handle from './resizable-handle.svelte'; +import PaneGroup from './resizable-pane-group.svelte'; + +export { + PaneGroup, + Pane, + Handle, + // + PaneGroup as ResizablePaneGroup, + Pane as ResizablePane, + Handle as ResizableHandle +}; diff --git a/frontend/src/lib/components/ui/resizable/resizable-handle.svelte b/frontend/src/lib/components/ui/resizable/resizable-handle.svelte new file mode 100644 index 0000000..c89ccd4 --- /dev/null +++ b/frontend/src/lib/components/ui/resizable/resizable-handle.svelte @@ -0,0 +1,30 @@ + + +div]:rotate-90', + className + )} + {...restProps} +> + {#if withHandle} +
    + +
    + {/if} +
    diff --git a/frontend/src/lib/components/ui/resizable/resizable-pane-group.svelte b/frontend/src/lib/components/ui/resizable/resizable-pane-group.svelte new file mode 100644 index 0000000..05e6a7d --- /dev/null +++ b/frontend/src/lib/components/ui/resizable/resizable-pane-group.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/scroll-area/index.ts b/frontend/src/lib/components/ui/scroll-area/index.ts new file mode 100644 index 0000000..971cdf4 --- /dev/null +++ b/frontend/src/lib/components/ui/scroll-area/index.ts @@ -0,0 +1,10 @@ +import Scrollbar from './scroll-area-scrollbar.svelte'; +import Root from './scroll-area.svelte'; + +export { + Root, + Scrollbar, + //, + Root as ScrollArea, + Scrollbar as ScrollAreaScrollbar +}; diff --git a/frontend/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte b/frontend/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte new file mode 100644 index 0000000..5862f16 --- /dev/null +++ b/frontend/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte @@ -0,0 +1,31 @@ + + + + {@render children?.()} + + diff --git a/frontend/src/lib/components/ui/scroll-area/scroll-area.svelte b/frontend/src/lib/components/ui/scroll-area/scroll-area.svelte new file mode 100644 index 0000000..0016f60 --- /dev/null +++ b/frontend/src/lib/components/ui/scroll-area/scroll-area.svelte @@ -0,0 +1,40 @@ + + + + + {@render children?.()} + + {#if orientation === 'vertical' || orientation === 'both'} + + {/if} + {#if orientation === 'horizontal' || orientation === 'both'} + + {/if} + + diff --git a/frontend/src/lib/components/ui/select/index.ts b/frontend/src/lib/components/ui/select/index.ts new file mode 100644 index 0000000..4b6e634 --- /dev/null +++ b/frontend/src/lib/components/ui/select/index.ts @@ -0,0 +1,37 @@ +import { Select as SelectPrimitive } from 'bits-ui'; + +import Group from './select-group.svelte'; +import Label from './select-label.svelte'; +import Item from './select-item.svelte'; +import Content from './select-content.svelte'; +import Trigger from './select-trigger.svelte'; +import Separator from './select-separator.svelte'; +import ScrollDownButton from './select-scroll-down-button.svelte'; +import ScrollUpButton from './select-scroll-up-button.svelte'; +import GroupHeading from './select-group-heading.svelte'; + +const Root = SelectPrimitive.Root; + +export { + Root, + Group, + Label, + Item, + Content, + Trigger, + Separator, + ScrollDownButton, + ScrollUpButton, + GroupHeading, + // + Root as Select, + Group as SelectGroup, + Label as SelectLabel, + Item as SelectItem, + Content as SelectContent, + Trigger as SelectTrigger, + Separator as SelectSeparator, + ScrollDownButton as SelectScrollDownButton, + ScrollUpButton as SelectScrollUpButton, + GroupHeading as SelectGroupHeading +}; diff --git a/frontend/src/lib/components/ui/select/select-content.svelte b/frontend/src/lib/components/ui/select/select-content.svelte new file mode 100644 index 0000000..3ddda2e --- /dev/null +++ b/frontend/src/lib/components/ui/select/select-content.svelte @@ -0,0 +1,40 @@ + + + + + + + {@render children?.()} + + + + diff --git a/frontend/src/lib/components/ui/select/select-group-heading.svelte b/frontend/src/lib/components/ui/select/select-group-heading.svelte new file mode 100644 index 0000000..ebd2f64 --- /dev/null +++ b/frontend/src/lib/components/ui/select/select-group-heading.svelte @@ -0,0 +1,21 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/select/select-group.svelte b/frontend/src/lib/components/ui/select/select-group.svelte new file mode 100644 index 0000000..64db383 --- /dev/null +++ b/frontend/src/lib/components/ui/select/select-group.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/select/select-item.svelte b/frontend/src/lib/components/ui/select/select-item.svelte new file mode 100644 index 0000000..c5de4b0 --- /dev/null +++ b/frontend/src/lib/components/ui/select/select-item.svelte @@ -0,0 +1,38 @@ + + + + {#snippet children({ selected, highlighted })} + + {#if selected} + + {/if} + + {#if childrenProp} + {@render childrenProp({ selected, highlighted })} + {:else} + {label || value} + {/if} + {/snippet} + diff --git a/frontend/src/lib/components/ui/select/select-label.svelte b/frontend/src/lib/components/ui/select/select-label.svelte new file mode 100644 index 0000000..464f60d --- /dev/null +++ b/frontend/src/lib/components/ui/select/select-label.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte b/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte new file mode 100644 index 0000000..ea11054 --- /dev/null +++ b/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte @@ -0,0 +1,20 @@ + + + + + diff --git a/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte b/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte new file mode 100644 index 0000000..0cb691e --- /dev/null +++ b/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte @@ -0,0 +1,20 @@ + + + + + diff --git a/frontend/src/lib/components/ui/select/select-separator.svelte b/frontend/src/lib/components/ui/select/select-separator.svelte new file mode 100644 index 0000000..810efbb --- /dev/null +++ b/frontend/src/lib/components/ui/select/select-separator.svelte @@ -0,0 +1,18 @@ + + + diff --git a/frontend/src/lib/components/ui/select/select-trigger.svelte b/frontend/src/lib/components/ui/select/select-trigger.svelte new file mode 100644 index 0000000..ea6e40b --- /dev/null +++ b/frontend/src/lib/components/ui/select/select-trigger.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/frontend/src/lib/components/ui/separator/index.ts b/frontend/src/lib/components/ui/separator/index.ts new file mode 100644 index 0000000..8ac60ed --- /dev/null +++ b/frontend/src/lib/components/ui/separator/index.ts @@ -0,0 +1,7 @@ +import Root from './separator.svelte'; + +export { + Root, + // + Root as Separator +}; diff --git a/frontend/src/lib/components/ui/separator/separator.svelte b/frontend/src/lib/components/ui/separator/separator.svelte new file mode 100644 index 0000000..5cb8e9e --- /dev/null +++ b/frontend/src/lib/components/ui/separator/separator.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/sheet/index.ts b/frontend/src/lib/components/ui/sheet/index.ts new file mode 100644 index 0000000..511ba01 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/sheet/sheet-close.svelte b/frontend/src/lib/components/ui/sheet/sheet-close.svelte new file mode 100644 index 0000000..b039558 --- /dev/null +++ b/frontend/src/lib/components/ui/sheet/sheet-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/sheet/sheet-content.svelte b/frontend/src/lib/components/ui/sheet/sheet-content.svelte new file mode 100644 index 0000000..d7b39fe --- /dev/null +++ b/frontend/src/lib/components/ui/sheet/sheet-content.svelte @@ -0,0 +1,58 @@ + + + + + + + + {@render children?.()} + + + Close + + + diff --git a/frontend/src/lib/components/ui/sheet/sheet-description.svelte b/frontend/src/lib/components/ui/sheet/sheet-description.svelte new file mode 100644 index 0000000..247fa21 --- /dev/null +++ b/frontend/src/lib/components/ui/sheet/sheet-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/sheet/sheet-footer.svelte b/frontend/src/lib/components/ui/sheet/sheet-footer.svelte new file mode 100644 index 0000000..0d3a60b --- /dev/null +++ b/frontend/src/lib/components/ui/sheet/sheet-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sheet/sheet-header.svelte b/frontend/src/lib/components/ui/sheet/sheet-header.svelte new file mode 100644 index 0000000..6435936 --- /dev/null +++ b/frontend/src/lib/components/ui/sheet/sheet-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sheet/sheet-overlay.svelte b/frontend/src/lib/components/ui/sheet/sheet-overlay.svelte new file mode 100644 index 0000000..8dd1052 --- /dev/null +++ b/frontend/src/lib/components/ui/sheet/sheet-overlay.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/sheet/sheet-title.svelte b/frontend/src/lib/components/ui/sheet/sheet-title.svelte new file mode 100644 index 0000000..6395cb2 --- /dev/null +++ b/frontend/src/lib/components/ui/sheet/sheet-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/sheet/sheet-trigger.svelte b/frontend/src/lib/components/ui/sheet/sheet-trigger.svelte new file mode 100644 index 0000000..2fc9651 --- /dev/null +++ b/frontend/src/lib/components/ui/sheet/sheet-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/ui/sidebar/constants.ts b/frontend/src/lib/components/ui/sidebar/constants.ts new file mode 100644 index 0000000..2d3bbfb --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/sidebar/context.svelte.ts b/frontend/src/lib/components/ui/sidebar/context.svelte.ts new file mode 100644 index 0000000..3f0df26 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/sidebar/index.ts b/frontend/src/lib/components/ui/sidebar/index.ts new file mode 100644 index 0000000..f12a0c7 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/sidebar/sidebar-content.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-content.svelte new file mode 100644 index 0000000..730c86f --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-content.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-footer.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-footer.svelte new file mode 100644 index 0000000..c042c4f --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-footer.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-group-action.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-group-action.svelte new file mode 100644 index 0000000..c28e5b5 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-group-action.svelte @@ -0,0 +1,36 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} + +{/if} diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-group-content.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-group-content.svelte new file mode 100644 index 0000000..a28a6ec --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-group-content.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-group-label.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-group-label.svelte new file mode 100644 index 0000000..9423771 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/sidebar/sidebar-group.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-group.svelte new file mode 100644 index 0000000..95d5a62 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-group.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-header.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-header.svelte new file mode 100644 index 0000000..0e32137 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-header.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-input.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-input.svelte new file mode 100644 index 0000000..f7c54d8 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-input.svelte @@ -0,0 +1,21 @@ + + + diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-inset.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-inset.svelte new file mode 100644 index 0000000..05a0d2d --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-inset.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-menu-action.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-menu-action.svelte new file mode 100644 index 0000000..bd886e4 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-menu-action.svelte @@ -0,0 +1,43 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} + +{/if} diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte new file mode 100644 index 0000000..583e1c7 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte @@ -0,0 +1,29 @@ + + +
    + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-menu-button.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-menu-button.svelte new file mode 100644 index 0000000..36a3d36 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/sidebar/sidebar-menu-item.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-menu-item.svelte new file mode 100644 index 0000000..58bf5a8 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-menu-item.svelte @@ -0,0 +1,21 @@ + + +
  • + {@render children?.()} +
  • diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte new file mode 100644 index 0000000..29df389 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte @@ -0,0 +1,36 @@ + + +
    + {#if showIcon} + + {/if} + + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte new file mode 100644 index 0000000..0083db8 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte new file mode 100644 index 0000000..2c9994e --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte @@ -0,0 +1,21 @@ + + +
  • + {@render children?.()} +
  • diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte new file mode 100644 index 0000000..45fee9f --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte @@ -0,0 +1,25 @@ + + +
      + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-menu.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-menu.svelte new file mode 100644 index 0000000..7400ed9 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-menu.svelte @@ -0,0 +1,21 @@ + + +
      + {@render children?.()} +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-provider.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-provider.svelte new file mode 100644 index 0000000..fe18948 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-provider.svelte @@ -0,0 +1,53 @@ + + + + + +
    + {@render children?.()} +
    +
    diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-rail.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-rail.svelte new file mode 100644 index 0000000..18f673d --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-rail.svelte @@ -0,0 +1,36 @@ + + + diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-separator.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-separator.svelte new file mode 100644 index 0000000..0417bf5 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-separator.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/sidebar/sidebar-trigger.svelte b/frontend/src/lib/components/ui/sidebar/sidebar-trigger.svelte new file mode 100644 index 0000000..da44cd5 --- /dev/null +++ b/frontend/src/lib/components/ui/sidebar/sidebar-trigger.svelte @@ -0,0 +1,35 @@ + + + diff --git a/frontend/src/lib/components/ui/sidebar/sidebar.svelte b/frontend/src/lib/components/ui/sidebar/sidebar.svelte new file mode 100644 index 0000000..fd9fdb7 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/skeleton/index.ts b/frontend/src/lib/components/ui/skeleton/index.ts new file mode 100644 index 0000000..7913720 --- /dev/null +++ b/frontend/src/lib/components/ui/skeleton/index.ts @@ -0,0 +1,7 @@ +import Root from './skeleton.svelte'; + +export { + Root, + // + Root as Skeleton +}; diff --git a/frontend/src/lib/components/ui/skeleton/skeleton.svelte b/frontend/src/lib/components/ui/skeleton/skeleton.svelte new file mode 100644 index 0000000..46879e6 --- /dev/null +++ b/frontend/src/lib/components/ui/skeleton/skeleton.svelte @@ -0,0 +1,17 @@ + + +
    diff --git a/frontend/src/lib/components/ui/slider/index.ts b/frontend/src/lib/components/ui/slider/index.ts new file mode 100644 index 0000000..4f9f74c --- /dev/null +++ b/frontend/src/lib/components/ui/slider/index.ts @@ -0,0 +1,7 @@ +import Root from './slider.svelte'; + +export { + Root, + // + Root as Slider +}; diff --git a/frontend/src/lib/components/ui/slider/slider.svelte b/frontend/src/lib/components/ui/slider/slider.svelte new file mode 100644 index 0000000..243ec1d --- /dev/null +++ b/frontend/src/lib/components/ui/slider/slider.svelte @@ -0,0 +1,52 @@ + + + + + {#snippet children({ thumbs })} + + + + {#each thumbs as thumb (thumb)} + + {/each} + {/snippet} + diff --git a/frontend/src/lib/components/ui/sonner/index.ts b/frontend/src/lib/components/ui/sonner/index.ts new file mode 100644 index 0000000..fcaf06b --- /dev/null +++ b/frontend/src/lib/components/ui/sonner/index.ts @@ -0,0 +1 @@ +export { default as Toaster } from './sonner.svelte'; diff --git a/frontend/src/lib/components/ui/sonner/sonner.svelte b/frontend/src/lib/components/ui/sonner/sonner.svelte new file mode 100644 index 0000000..877d00f --- /dev/null +++ b/frontend/src/lib/components/ui/sonner/sonner.svelte @@ -0,0 +1,13 @@ + + + diff --git a/frontend/src/lib/components/ui/switch/index.ts b/frontend/src/lib/components/ui/switch/index.ts new file mode 100644 index 0000000..4678a5a --- /dev/null +++ b/frontend/src/lib/components/ui/switch/index.ts @@ -0,0 +1,7 @@ +import Root from './switch.svelte'; + +export { + Root, + // + Root as Switch +}; diff --git a/frontend/src/lib/components/ui/switch/switch.svelte b/frontend/src/lib/components/ui/switch/switch.svelte new file mode 100644 index 0000000..38a0fc5 --- /dev/null +++ b/frontend/src/lib/components/ui/switch/switch.svelte @@ -0,0 +1,29 @@ + + + + + diff --git a/frontend/src/lib/components/ui/table/index.ts b/frontend/src/lib/components/ui/table/index.ts new file mode 100644 index 0000000..9c61b17 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/table/table-body.svelte b/frontend/src/lib/components/ui/table/table-body.svelte new file mode 100644 index 0000000..4343eab --- /dev/null +++ b/frontend/src/lib/components/ui/table/table-body.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/table/table-caption.svelte b/frontend/src/lib/components/ui/table/table-caption.svelte new file mode 100644 index 0000000..370f708 --- /dev/null +++ b/frontend/src/lib/components/ui/table/table-caption.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/table/table-cell.svelte b/frontend/src/lib/components/ui/table/table-cell.svelte new file mode 100644 index 0000000..eeea263 --- /dev/null +++ b/frontend/src/lib/components/ui/table/table-cell.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/table/table-footer.svelte b/frontend/src/lib/components/ui/table/table-footer.svelte new file mode 100644 index 0000000..c620aa5 --- /dev/null +++ b/frontend/src/lib/components/ui/table/table-footer.svelte @@ -0,0 +1,20 @@ + + +tr]:last:border-b-0', className)} + {...restProps} +> + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/table/table-head.svelte b/frontend/src/lib/components/ui/table/table-head.svelte new file mode 100644 index 0000000..69cc9fa --- /dev/null +++ b/frontend/src/lib/components/ui/table/table-head.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/table/table-header.svelte b/frontend/src/lib/components/ui/table/table-header.svelte new file mode 100644 index 0000000..a345a6a --- /dev/null +++ b/frontend/src/lib/components/ui/table/table-header.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/table/table-row.svelte b/frontend/src/lib/components/ui/table/table-row.svelte new file mode 100644 index 0000000..ffd2873 --- /dev/null +++ b/frontend/src/lib/components/ui/table/table-row.svelte @@ -0,0 +1,23 @@ + + +svelte-css-wrapper]:[&>th,td]:bg-muted/50', + className + )} + {...restProps} +> + {@render children?.()} + diff --git a/frontend/src/lib/components/ui/table/table.svelte b/frontend/src/lib/components/ui/table/table.svelte new file mode 100644 index 0000000..8c49f5d --- /dev/null +++ b/frontend/src/lib/components/ui/table/table.svelte @@ -0,0 +1,22 @@ + + +
    + + {@render children?.()} +
    +
    diff --git a/frontend/src/lib/components/ui/tabs/index.ts b/frontend/src/lib/components/ui/tabs/index.ts new file mode 100644 index 0000000..58b946f --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/index.ts @@ -0,0 +1,16 @@ +import Root from './tabs.svelte'; +import Content from './tabs-content.svelte'; +import List from './tabs-list.svelte'; +import Trigger from './tabs-trigger.svelte'; + +export { + Root, + Content, + List, + Trigger, + // + Root as Tabs, + Content as TabsContent, + List as TabsList, + Trigger as TabsTrigger +}; diff --git a/frontend/src/lib/components/ui/tabs/tabs-content.svelte b/frontend/src/lib/components/ui/tabs/tabs-content.svelte new file mode 100644 index 0000000..ba5a9be --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs-content.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/tabs/tabs-list.svelte b/frontend/src/lib/components/ui/tabs/tabs-list.svelte new file mode 100644 index 0000000..e148c81 --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs-list.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/tabs/tabs-trigger.svelte b/frontend/src/lib/components/ui/tabs/tabs-trigger.svelte new file mode 100644 index 0000000..55135d9 --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs-trigger.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/tabs/tabs.svelte b/frontend/src/lib/components/ui/tabs/tabs.svelte new file mode 100644 index 0000000..6188849 --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/textarea/index.ts b/frontend/src/lib/components/ui/textarea/index.ts new file mode 100644 index 0000000..9262656 --- /dev/null +++ b/frontend/src/lib/components/ui/textarea/index.ts @@ -0,0 +1,7 @@ +import Root from './textarea.svelte'; + +export { + Root, + // + Root as Textarea +}; diff --git a/frontend/src/lib/components/ui/textarea/textarea.svelte b/frontend/src/lib/components/ui/textarea/textarea.svelte new file mode 100644 index 0000000..c410cdd --- /dev/null +++ b/frontend/src/lib/components/ui/textarea/textarea.svelte @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/lib/components/ui/toggle-group/index.ts b/frontend/src/lib/components/ui/toggle-group/index.ts new file mode 100644 index 0000000..e564e12 --- /dev/null +++ b/frontend/src/lib/components/ui/toggle-group/index.ts @@ -0,0 +1,10 @@ +import Root from './toggle-group.svelte'; +import Item from './toggle-group-item.svelte'; + +export { + Root, + Item, + // + Root as ToggleGroup, + Item as ToggleGroupItem +}; diff --git a/frontend/src/lib/components/ui/toggle-group/toggle-group-item.svelte b/frontend/src/lib/components/ui/toggle-group/toggle-group-item.svelte new file mode 100644 index 0000000..b409801 --- /dev/null +++ b/frontend/src/lib/components/ui/toggle-group/toggle-group-item.svelte @@ -0,0 +1,34 @@ + + + diff --git a/frontend/src/lib/components/ui/toggle-group/toggle-group.svelte b/frontend/src/lib/components/ui/toggle-group/toggle-group.svelte new file mode 100644 index 0000000..3568ac7 --- /dev/null +++ b/frontend/src/lib/components/ui/toggle-group/toggle-group.svelte @@ -0,0 +1,47 @@ + + + + + + diff --git a/frontend/src/lib/components/ui/toggle/index.ts b/frontend/src/lib/components/ui/toggle/index.ts new file mode 100644 index 0000000..ac1b32a --- /dev/null +++ b/frontend/src/lib/components/ui/toggle/index.ts @@ -0,0 +1,13 @@ +import Root from './toggle.svelte'; +export { + toggleVariants, + type ToggleSize, + type ToggleVariant, + type ToggleVariants +} from './toggle.svelte'; + +export { + Root, + // + Root as Toggle +}; diff --git a/frontend/src/lib/components/ui/toggle/toggle.svelte b/frontend/src/lib/components/ui/toggle/toggle.svelte new file mode 100644 index 0000000..cc5ea6a --- /dev/null +++ b/frontend/src/lib/components/ui/toggle/toggle.svelte @@ -0,0 +1,52 @@ + + + + + diff --git a/frontend/src/lib/components/ui/tooltip/index.ts b/frontend/src/lib/components/ui/tooltip/index.ts new file mode 100644 index 0000000..22d2ab4 --- /dev/null +++ b/frontend/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/frontend/src/lib/components/ui/tooltip/tooltip-content.svelte b/frontend/src/lib/components/ui/tooltip/tooltip-content.svelte new file mode 100644 index 0000000..523d1f8 --- /dev/null +++ b/frontend/src/lib/components/ui/tooltip/tooltip-content.svelte @@ -0,0 +1,47 @@ + + + + + {@render children?.()} + + {#snippet child({ props })} +
    + {/snippet} +
    +
    +
    diff --git a/frontend/src/lib/components/ui/tooltip/tooltip-trigger.svelte b/frontend/src/lib/components/ui/tooltip/tooltip-trigger.svelte new file mode 100644 index 0000000..c524f13 --- /dev/null +++ b/frontend/src/lib/components/ui/tooltip/tooltip-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/components/version-switcher.svelte b/frontend/src/lib/components/version-switcher.svelte new file mode 100644 index 0000000..644a04a --- /dev/null +++ b/frontend/src/lib/components/version-switcher.svelte @@ -0,0 +1,48 @@ + + + + + + + {#snippet child({ props })} + +
    + +
    +
    + Documentation + v{selectedVersion} +
    + +
    + {/snippet} +
    + + {#each versions as version (version)} + (selectedVersion = version)}> + v{version} + {#if version === selectedVersion} + + {/if} + + {/each} + +
    +
    +
    diff --git a/frontend/src/lib/database.types.ts b/frontend/src/lib/database.types.ts new file mode 100644 index 0000000..431d238 --- /dev/null +++ b/frontend/src/lib/database.types.ts @@ -0,0 +1,239 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[] + +export type Database = { + // Allows to automatically instanciate createClient with right options + // instead of createClient(URL, KEY) + __InternalSupabase: { + PostgrestVersion: "12.2.12 (cd3cf9e)" + } + public: { + Tables: { + queue: { + Row: { + entry: string + interted_at: string + owner: string + played: boolean + song: string + space_id: string + } + Insert: { + entry?: string + interted_at?: string + owner: string + played?: boolean + song: string + space_id: string + } + Update: { + entry?: string + interted_at?: string + owner?: string + played?: boolean + song?: string + space_id?: string + } + Relationships: [ + { + foreignKeyName: "queue_space_id_fkey" + columns: ["space_id"] + isOneToOne: false + referencedRelation: "spaces" + referencedColumns: ["space_id"] + }, + ] + } + spaces: { + Row: { + currently_playing: string | null + name: string | null + owner: string + rules: string | null + space_id: string + } + Insert: { + currently_playing?: string | null + name?: string | null + owner: string + rules?: string | null + space_id?: string + } + Update: { + currently_playing?: string | null + name?: string | null + owner?: string + rules?: string | null + space_id?: string + } + Relationships: [] + } + votes: { + Row: { + date: string + owner: string + queue_entry: string + } + Insert: { + date?: string + owner: string + queue_entry: string + } + Update: { + date?: string + owner?: string + queue_entry?: string + } + Relationships: [ + { + foreignKeyName: "votes_queue_entry_fkey" + columns: ["queue_entry"] + isOneToOne: false + referencedRelation: "queue" + referencedColumns: ["entry"] + }, + ] + } + } + Views: { + [_ in never]: never + } + Functions: { + [_ in never]: never + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + +type DatabaseWithoutInternals = Omit + +type DefaultSchema = DatabaseWithoutInternals[Extract] + +export type Tables< + DefaultSchemaTableNameOrOptions extends + | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R + } + ? R + : never + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & + DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & + DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R + } + ? R + : never + : never + +export type TablesInsert< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I + } + ? I + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I + } + ? I + : never + : never + +export type TablesUpdate< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U + } + ? U + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Update: infer U + } + ? U + : never + : never + +export type Enums< + DefaultSchemaEnumNameOrOptions extends + | keyof DefaultSchema["Enums"] + | { schema: keyof DatabaseWithoutInternals }, + EnumName extends DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] + : never = never, +> = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] + ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] + : never + +export type CompositeTypes< + PublicCompositeTypeNameOrOptions extends + | keyof DefaultSchema["CompositeTypes"] + | { schema: keyof DatabaseWithoutInternals }, + CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + : never = never, +> = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] + ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never + +export const Constants = { + public: { + Enums: {}, + }, +} as const diff --git a/frontend/src/lib/env.ts b/frontend/src/lib/env.ts new file mode 100644 index 0000000..013a1c5 --- /dev/null +++ b/frontend/src/lib/env.ts @@ -0,0 +1,26 @@ +export const get_base_url = () => window.location.origin; + +export const BACKEND_URL = 'https://supa-duo-backend.up.railway.app'; + +// All available Spotify scopes +export const SCOPES = [ + 'ugc-image-upload', + 'user-read-playback-state', + 'user-modify-playback-state', + 'user-read-currently-playing', + 'streaming', + 'app-remote-control', + 'user-read-email', + 'user-read-private', + 'playlist-read-collaborative', + 'playlist-modify-public', + 'playlist-read-private', + 'playlist-modify-private', + 'user-library-modify', + 'user-library-read', + 'user-top-read', + 'user-read-playback-position', + 'user-read-recently-played', + 'user-follow-read', + 'user-follow-modify' +].join(' '); diff --git a/frontend/src/lib/hooks/is-mobile.svelte.ts b/frontend/src/lib/hooks/is-mobile.svelte.ts new file mode 100644 index 0000000..2dfb0eb --- /dev/null +++ b/frontend/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/frontend/src/lib/index.ts b/frontend/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/frontend/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/frontend/src/lib/spotify.ts b/frontend/src/lib/spotify.ts new file mode 100644 index 0000000..5d4331e --- /dev/null +++ b/frontend/src/lib/spotify.ts @@ -0,0 +1,100 @@ +import type { GetTrackInput, GetTrackOutput } from 'backend/api'; +import { BACKEND_URL } from './env'; +import type { SpotifyTrack } from 'backend/spotify'; + +export async function play_track(token: string, track_id: string | undefined): Promise { + if (track_id === undefined) { + // Stop current playback + const response = await fetch('https://api.spotify.com/v1/me/player/pause', { + method: 'PUT', + headers: { + Authorization: `Bearer ${token}` + } + }); + + if (!response.ok) { + throw new Error( + `Failed to stop track: ${response.status} ${response.statusText} ${await response.text()}` + ); + } + return; + } + + const response = await fetch('https://api.spotify.com/v1/me/player/play', { + method: 'PUT', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + uris: [`spotify:track:${track_id}`] + }) + }); + + if (!response.ok) { + throw new Error( + `Failed to play track: ${response.status} ${response.statusText} ${await response.text()}` + ); + } +} + +export async function get_current_playback(token: string) { + const response = await fetch('https://api.spotify.com/v1/me/player', { + headers: { + Authorization: `Bearer ${token}` + } + }); + if (response.status === 204) return null; // No active device + if (!response.ok) throw new Error(`Failed to get playback state: ${response.status}`); + return response.json(); +} + +export async function get_track(song: string): Promise { + try { + if (song == '') { + throw 'hehe'; + } + // Check localStorage cache first + const cacheKey = `spotify_track_${song}`; + const cached = localStorage.getItem(cacheKey); + + if (cached) { + try { + const cachedData = JSON.parse(cached); + // Check if cache is still valid (1 hour expiry) + if (Date.now() - cachedData.timestamp < 3600000) { + return cachedData.data; + } + } catch (e) { + // Invalid cache data, continue to fetch + } + } + + const d = await fetch(`${BACKEND_URL}/track`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ trackId: song } satisfies GetTrackInput) + }); + + // Cache the response + if (d.ok) { + const result = await d.json(); + localStorage.setItem( + cacheKey, + JSON.stringify({ + data: result, + timestamp: Date.now() + }) + ); + return result; + } + const result = (await d.json()) as GetTrackOutput; + return result.track; + } catch (error) { + console.error(error); + } + await new Promise(() => {}); + throw 'unreachable'; +} diff --git a/frontend/src/lib/stores.ts b/frontend/src/lib/stores.ts new file mode 100644 index 0000000..3cb014c --- /dev/null +++ b/frontend/src/lib/stores.ts @@ -0,0 +1,58 @@ +import { writable } from 'svelte/store'; + +function createPersistedStore(key: string, initialValue: T, expirySeconds?: number) { + const { subscribe, set, update } = writable(initialValue); + + // Load from localStorage on initialization + if (typeof window !== 'undefined') { + const stored = localStorage.getItem(key); + if (stored) { + try { + const parsedData = JSON.parse(stored); + // Check if data has expiry information + if (expirySeconds && parsedData.expiry && parsedData.expiry < Date.now()) { + // Data has expired, use initial value + localStorage.removeItem(key); + set(initialValue); + } else { + // Use stored value (either no expiry or not expired) + const value = + expirySeconds && parsedData.value !== undefined + ? parsedData.value + : parsedData; + set(value); + } + } catch (e) { + // If parsing fails, use initial value + set(initialValue); + } + } + } + + return { + subscribe, + set: (value: T) => { + if (typeof window !== 'undefined') { + const dataToStore = expirySeconds + ? { value, expiry: Date.now() + expirySeconds * 1000 } + : value; + localStorage.setItem(key, JSON.stringify(dataToStore)); + } + set(value); + }, + update: (updater: (value: T) => T) => { + update((current) => { + const newValue = updater(current); + if (typeof window !== 'undefined') { + const dataToStore = expirySeconds + ? { value: newValue, expiry: Date.now() + expirySeconds * 1000 } + : newValue; + localStorage.setItem(key, JSON.stringify(dataToStore)); + } + return newValue; + }); + } + }; +} + +export const spotify_token = createPersistedStore('spotify_token_2', null, 3600); diff --git a/frontend/src/lib/supabase.ts b/frontend/src/lib/supabase.ts new file mode 100644 index 0000000..d3df8dd --- /dev/null +++ b/frontend/src/lib/supabase.ts @@ -0,0 +1,51 @@ +import { createClient } from '@supabase/supabase-js'; +import type { Database } from './database.types'; +import { writable } from 'svelte/store'; + +export const supabase = createClient( + 'https://htdssaqidnitlbswknqw.supabase.co', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imh0ZHNzYXFpZG5pdGxic3drbnF3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQwNTg4MDIsImV4cCI6MjA2OTYzNDgwMn0.zHrVf6kzr-uZAqeDnB0ZTbgCTaXwtvEGi95_8PQGh5g' +); + +// Store for the current user_id +export const current_user_id = writable(null); + +async function ensure_anonymous_auth() { + try { + // Check current session first + const { + data: { session } + } = await supabase.auth.getSession(); + + if (!session) { + const { data, error } = await supabase.auth.signInAnonymously(); + if (error) { + console.error('Anonymous sign-in failed:', error); + // Retry after a delay + setTimeout(ensure_anonymous_auth, 5000); + } else { + current_user_id.set(data.user?.id || null); + } + } else { + current_user_id.set(session.user?.id || null); + } + } catch (err) { + console.error('Error during anonymous auth check:', err); + // Retry after a delay + setTimeout(ensure_anonymous_auth, 5000); + } +} + +ensure_anonymous_auth(); + +// Listen for auth state changes and reconnect if needed +const { + data: { subscription } +} = supabase.auth.onAuthStateChange((event, session) => { + if (event === 'SIGNED_OUT' || !session) { + current_user_id.set(null); + ensure_anonymous_auth(); + } else { + current_user_id.set(session.user?.id || null); + } +}); diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts new file mode 100644 index 0000000..89521bd --- /dev/null +++ b/frontend/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/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte new file mode 100644 index 0000000..dc5b5c9 --- /dev/null +++ b/frontend/src/routes/+layout.svelte @@ -0,0 +1,21 @@ + + + + + + +{#if $current_user_id} +
    +
    + {@render children?.()} +
    +
    +{:else} + Not logged in +{/if} diff --git a/frontend/src/routes/+layout.ts b/frontend/src/routes/+layout.ts new file mode 100644 index 0000000..a3d1578 --- /dev/null +++ b/frontend/src/routes/+layout.ts @@ -0,0 +1 @@ +export const ssr = false; diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/routes/+page.ts b/frontend/src/routes/+page.ts new file mode 100644 index 0000000..4e93d5d --- /dev/null +++ b/frontend/src/routes/+page.ts @@ -0,0 +1,3 @@ +import { redirect } from '@sveltejs/kit'; + +redirect(302, '/create'); diff --git a/frontend/src/routes/create/+page.svelte b/frontend/src/routes/create/+page.svelte new file mode 100644 index 0000000..6a9314d --- /dev/null +++ b/frontend/src/routes/create/+page.svelte @@ -0,0 +1,127 @@ + + +
    + {#if !$spotify_token} +

    Spotify Integration

    + + + +

    + {connection_status === 'error' ? 'Error connecting' : ''} +

    + {:else} +
    +

    Space Name

    + +
    +
    +

    Rules

    + +
    + + {/if} +
    diff --git a/frontend/src/routes/space/[space]/+layout.svelte b/frontend/src/routes/space/[space]/+layout.svelte new file mode 100644 index 0000000..c8f1997 --- /dev/null +++ b/frontend/src/routes/space/[space]/+layout.svelte @@ -0,0 +1,155 @@ + + +{@render children?.()} diff --git a/frontend/src/routes/space/[space]/+layout.ts b/frontend/src/routes/space/[space]/+layout.ts new file mode 100644 index 0000000..d783167 --- /dev/null +++ b/frontend/src/routes/space/[space]/+layout.ts @@ -0,0 +1,37 @@ +import { supabase } from '$lib/supabase'; +import { writable, type Writable } from 'svelte/store'; +import type { LayoutLoad } from './$types'; +import { queue_query, sort_queue, type QueueType } from './functions'; + +export const load: LayoutLoad = async ({ params: { space } }) => { + const { data: space_data } = await supabase + .from('spaces') + .select('*') + .eq('space_id', space) + .single(); + + const { data: queue_data } = await supabase + .from('queue') + .select(queue_query) + .eq('space_id', space); + + const create_sorted_queue_store = (initial_value: QueueType[]) => { + const { subscribe, set, update } = writable(sort_queue(initial_value)); + + return { + subscribe, + set: (value: QueueType[]) => set(sort_queue(value)), + update: (updater: (value: QueueType[]) => QueueType[]) => + update((current) => { + return sort_queue(updater(current)); + }) + }; + }; + + const queue_store: Writable = create_sorted_queue_store(queue_data ?? []); + + return { + space: writable(space_data), + queue: queue_store + }; +}; diff --git a/frontend/src/routes/space/[space]/+page.svelte b/frontend/src/routes/space/[space]/+page.svelte new file mode 100644 index 0000000..2c32228 --- /dev/null +++ b/frontend/src/routes/space/[space]/+page.svelte @@ -0,0 +1,387 @@ + + +
    + {#if is_owner} + + {/if} + {#if is_owner && !$spotify_token} + login + {:else if $queue} + {#if $space && space_id} +
    +
    + {#if !is_owner} +
    + (can_vote = can_vote_p)} + /> +
    + {/if} +
    +

    + {$space.name} +

    +

    + Welcome to the tunes +

    +
    +
    +
    + {#if is_owner} +
    + {#await qr_code} + Loading... + {:then qr_code_svg} + + {@html qr_code_svg} + + {/await} +
    + {/if} +
    +
    +
    + {#if filtered_queue.length > 0} + {#each filtered_queue as queue_entry, i (queue_entry.entry)} + {@const own_vote = queue_entry.votes?.find( + (v) => + v.owner == $current_user_id! && + v.queue_entry == queue_entry.entry + )} + {@const own_song = queue_entry.owner == $current_user_id!} +
    + + {#if own_song || is_owner} + + {/if} + + + +
    + {/each} + {:else} +
    +

    + Queued songs will appear here... +

    +
    + {/if} +
    +
    +
    + + {#if !is_owner} + {#await songs_prom then songss} + {#if songss.length > 0 && filtered_queue.length > 0} + {@const userSongIndex = filtered_queue.findIndex( + (entry) => entry.owner === $current_user_id + )} + {#if userSongIndex !== -1} + {@const songsBeforeUser = songss.slice(0, userSongIndex)} + {@const totalDurationMs = songsBeforeUser.reduce( + (sum, song) => sum + song.duration_ms, + 0 + )} + {@const totalSeconds = Math.ceil(totalDurationMs / 1000)} + {@const minutes = Math.floor(totalSeconds / 60)} + {#if minutes > 0} +
    +
    + Your song will play in roughly {minutes}m +
    +
    + {/if} + {/if} + {/if} + {/await} + {/if} +
    + + {#if is_owner} + + {/if} + +
    + + {#if !is_owner} + + {/if} + {:else} + Space not found + {/if} + {:else} + Error + {/if} +
    diff --git a/frontend/src/routes/space/[space]/AddSongs.svelte b/frontend/src/routes/space/[space]/AddSongs.svelte new file mode 100644 index 0000000..93494b1 --- /dev/null +++ b/frontend/src/routes/space/[space]/AddSongs.svelte @@ -0,0 +1,141 @@ + + + + + + + +
    +
    +
    + Search + Find tunes with vibes and add 'em! +
    + +
    + {#if loading == 'not_searched'} + + {:else if loading == 'loading'} + {#each ['', '', ''] as song, index} +
    + +
    + {/each} + {:else if loading == 'loaded' && results?.type == 'success' && results.songs.length == 0} + No results + {:else if !results} + Error loading :( + {:else if results.type == 'success'} + {#each results.songs as song, index (song.id)} + {@const songs = results.songs} +
    + + + +
    + {/each} + {:else} + {results.error} + {/if} +
    +
    +
    +
    +
    diff --git a/frontend/src/routes/space/[space]/Song.svelte b/frontend/src/routes/space/[space]/Song.svelte new file mode 100644 index 0000000..dc4fc94 --- /dev/null +++ b/frontend/src/routes/space/[space]/Song.svelte @@ -0,0 +1,72 @@ + + +
    +
    + {#await actual_song} + + {:then resolved_song} + Album cover + {/await} +
    + +
    +

    + {#await actual_song} + + {:then resolved_song} + {resolved_song.name} + {/await} +

    +

    + {#await actual_song} + + {:then resolved_song} + {resolved_song.artists.map((a) => a.name).join(', ')} + {/await} +

    +
    + {#if children} +
    + {@render children()} +
    + {/if} +
    diff --git a/frontend/src/routes/space/[space]/VoteCounter.svelte b/frontend/src/routes/space/[space]/VoteCounter.svelte new file mode 100644 index 0000000..35b3f6d --- /dev/null +++ b/frontend/src/routes/space/[space]/VoteCounter.svelte @@ -0,0 +1,130 @@ + + +
    +
    +
    + + + {Math.floor(count.current)} + +
    +
    +
    +
    diff --git a/frontend/src/routes/space/[space]/functions.ts b/frontend/src/routes/space/[space]/functions.ts new file mode 100644 index 0000000..c33d05f --- /dev/null +++ b/frontend/src/routes/space/[space]/functions.ts @@ -0,0 +1,180 @@ +import type { Database } from '$lib/database.types'; +import { supabase } from '$lib/supabase'; +import type { QueryData } from '@supabase/supabase-js'; +import type { Writable } from 'svelte/store'; + +export const queue_query = '*, votes:votes!votes_queue_entry_fkey(*)'; +const _dummy_query = supabase.from('queue').select(queue_query); +export type QueueType = QueryData[0]; + +export type SpaceType = Database['public']['Tables']['spaces']['Row']; + +export const sort_queue = (queue: QueueType[]): QueueType[] => { + return [...queue].sort((a, b) => { + // First sort by number of votes (descending) + const votes_a = a.votes?.length || 0; + const votes_b = b.votes?.length || 0; + + if (votes_a !== votes_b) { + return votes_b - votes_a; + } + + // Then sort by inserted_at time (ascending - earliest first) + const time_a = new Date(a.interted_at).getTime(); + const time_b = new Date(b.interted_at).getTime(); + + return time_a - time_b; + }); +}; + +type QueueInsert = Database['public']['Tables']['queue']['Row']; + +export const update_queue_element = async ( + list: Writable, + item: Database['public']['Tables']['queue']['Update'] +) => { + let success = false; + if (!item.entry) { + return success; + } + + list.update((queue) => + queue.map((queue_item) => + queue_item.entry === item.entry ? { ...queue_item, ...item } : queue_item + ) + ); + + const { data: update_data, error } = await supabase + .from('queue') + .update(item) + .eq('entry', item.entry) + .select() + .single(); + + if (error) console.error(error); + + success = !!update_data; + + return success; +}; + +export const edit_space = async ( + space_store: Writable, + item: Database['public']['Tables']['spaces']['Update'], + id: string +) => { + let success = false; + + space_store.update((space) => (space ? { ...space, ...item } : space)); + + const { data: update_data, error } = await supabase + .from('spaces') + .update(item) + .eq('space_id', id) + .select() + .single(); + + if (error) console.error(error); + + success = !!update_data; + + return success; +}; + +export const edit_queue = async ( + list: Writable, + item: QueueInsert, + action: 'remove' | 'add' +) => { + let success = false; + + switch (action) { + case 'add': + list.update((queue) => [ + ...queue, + { + ...item, + votes: [] + } as QueueType + ]); + const { data: insert_data } = await supabase + .from('queue') + .insert(item) + .select() + .single(); + success = !!insert_data; + break; + case 'remove': + list.update((queue) => queue.filter((queue_item) => queue_item.entry !== item.entry)); + const { data: delete_data } = await supabase + .from('queue') + .delete() + .eq('entry', item.entry ?? '') + .select() + .single(); + success = !!delete_data; + break; + } + + return success; +}; + +type VoteInsert = Database['public']['Tables']['votes']['Insert']; + +export const edit_vote = async ( + list: Writable, + item: VoteInsert, + action: 'remove' | 'add' +) => { + let success = false; + + switch (action) { + case 'add': + list.update((queue) => + queue.map((queue_item) => + queue_item.entry === item.queue_entry + ? { + ...queue_item, + votes: [ + ...queue_item.votes, + { + owner: item.owner, + queue_entry: item.queue_entry, + date: new Date().toISOString() + } + ] + } + : queue_item + ) + ); + const { data: insert_data } = await supabase + .from('votes') + .insert(item) + .select() + .single(); + success = !!insert_data; + break; + case 'remove': + list.update((queue) => + queue.map((queueItem) => + queueItem.entry === item.queue_entry + ? { + ...queueItem, + votes: queueItem.votes.filter((vote) => vote.owner !== item.owner) + } + : queueItem + ) + ); + const { data: delete_data } = await supabase + .from('votes') + .delete() + .match(item) + .select() + .single(); + success = !!delete_data; + + break; + } + + return success; +}; diff --git a/frontend/static/robots.txt b/frontend/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/frontend/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js new file mode 100644 index 0000000..3da2804 --- /dev/null +++ b/frontend/svelte.config.js @@ -0,0 +1,21 @@ +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(), + alias: { + backend: '../backend/types/*' + } + } +}; + +export default config; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..19034e0 --- /dev/null +++ b/frontend/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 + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..6ba65f7 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,11 @@ +import tailwindcss from '@tailwindcss/vite'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [tailwindcss(), sveltekit()], + server: { + host: '127.0.0.1', + port: 5173 + } +});