├── .env.example
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── README.md
├── components.json
├── drizzle.config.ts
├── eslint.config.js
├── package.json
├── playwright.config.ts
├── pnpm-lock.yaml
├── src
├── app.d.ts
├── app.html
├── hooks.server.ts
├── lib
│ ├── assets
│ │ ├── avatar.png
│ │ ├── logo.png
│ │ └── meta_image.png
│ ├── components
│ │ ├── PlaceholderPattern.svelte
│ │ ├── SEO.svelte
│ │ ├── SettingsNavbar.svelte
│ │ ├── Sidebar.svelte
│ │ ├── SidebarContent.svelte
│ │ ├── SidebarFooter.svelte
│ │ ├── SidebarHeader.svelte
│ │ ├── ThemeSwitcher.svelte
│ │ ├── Wrapper.svelte
│ │ └── ui
│ │ │ ├── accordion
│ │ │ ├── accordion-content.svelte
│ │ │ ├── accordion-item.svelte
│ │ │ ├── accordion-root.svelte
│ │ │ ├── accordion-trigger.svelte
│ │ │ └── index.ts
│ │ │ ├── alert-dialog
│ │ │ ├── alert-dialog-action.svelte
│ │ │ ├── alert-dialog-cancel.svelte
│ │ │ ├── alert-dialog-content.svelte
│ │ │ ├── alert-dialog-description.svelte
│ │ │ ├── alert-dialog-footer.svelte
│ │ │ ├── alert-dialog-header.svelte
│ │ │ ├── alert-dialog-overlay.svelte
│ │ │ ├── alert-dialog-portal.svelte
│ │ │ ├── alert-dialog-title.svelte
│ │ │ ├── alert-dialog-trigger.svelte
│ │ │ └── index.ts
│ │ │ ├── alert
│ │ │ ├── alert-description.svelte
│ │ │ ├── alert-title.svelte
│ │ │ ├── alert.svelte
│ │ │ └── index.ts
│ │ │ ├── aspect-ratio
│ │ │ ├── aspect-ratio.svelte
│ │ │ └── index.ts
│ │ │ ├── avatar
│ │ │ ├── avatar-fallback.svelte
│ │ │ ├── avatar-image.svelte
│ │ │ ├── avatar.svelte
│ │ │ └── index.ts
│ │ │ ├── badge
│ │ │ ├── badge.svelte
│ │ │ └── index.ts
│ │ │ ├── breadcrumb
│ │ │ ├── breadcrumb-ellipsis.svelte
│ │ │ ├── breadcrumb-item.svelte
│ │ │ ├── breadcrumb-link.svelte
│ │ │ ├── breadcrumb-list.svelte
│ │ │ ├── breadcrumb-page.svelte
│ │ │ ├── breadcrumb-separator.svelte
│ │ │ ├── breadcrumb.svelte
│ │ │ └── index.ts
│ │ │ ├── button
│ │ │ ├── button.svelte
│ │ │ └── index.ts
│ │ │ ├── calendar
│ │ │ ├── calendar-cell.svelte
│ │ │ ├── calendar-day.svelte
│ │ │ ├── calendar-grid-body.svelte
│ │ │ ├── calendar-grid-head.svelte
│ │ │ ├── calendar-grid-row.svelte
│ │ │ ├── calendar-grid.svelte
│ │ │ ├── calendar-head-cell.svelte
│ │ │ ├── calendar-header.svelte
│ │ │ ├── calendar-heading.svelte
│ │ │ ├── calendar-months.svelte
│ │ │ ├── calendar-next-button.svelte
│ │ │ ├── calendar-prev-button.svelte
│ │ │ ├── calendar.svelte
│ │ │ └── index.ts
│ │ │ ├── card
│ │ │ ├── card-action.svelte
│ │ │ ├── card-content.svelte
│ │ │ ├── card-description.svelte
│ │ │ ├── card-footer.svelte
│ │ │ ├── card-header.svelte
│ │ │ ├── card-title.svelte
│ │ │ ├── card.svelte
│ │ │ └── index.ts
│ │ │ ├── carousel
│ │ │ ├── carousel-content.svelte
│ │ │ ├── carousel-item.svelte
│ │ │ ├── carousel-next.svelte
│ │ │ ├── carousel-previous.svelte
│ │ │ ├── carousel.svelte
│ │ │ ├── context.ts
│ │ │ └── index.ts
│ │ │ ├── chart
│ │ │ ├── chart-container.svelte
│ │ │ ├── chart-style.svelte
│ │ │ ├── chart-tooltip.svelte
│ │ │ ├── chart-utils.ts
│ │ │ └── index.ts
│ │ │ ├── checkbox
│ │ │ ├── checkbox.svelte
│ │ │ └── index.ts
│ │ │ ├── collapsible
│ │ │ ├── collapsible-content.svelte
│ │ │ ├── collapsible-trigger.svelte
│ │ │ ├── collapsible.svelte
│ │ │ └── index.ts
│ │ │ ├── command
│ │ │ ├── command-dialog.svelte
│ │ │ ├── command-empty.svelte
│ │ │ ├── command-group.svelte
│ │ │ ├── command-input.svelte
│ │ │ ├── command-item.svelte
│ │ │ ├── command-link-item.svelte
│ │ │ ├── command-list.svelte
│ │ │ ├── command-separator.svelte
│ │ │ ├── command-shortcut.svelte
│ │ │ ├── command.svelte
│ │ │ └── index.ts
│ │ │ ├── context-menu
│ │ │ ├── context-menu-checkbox-item.svelte
│ │ │ ├── context-menu-content.svelte
│ │ │ ├── context-menu-group-heading.svelte
│ │ │ ├── context-menu-group.svelte
│ │ │ ├── context-menu-item.svelte
│ │ │ ├── context-menu-label.svelte
│ │ │ ├── context-menu-radio-group.svelte
│ │ │ ├── context-menu-radio-item.svelte
│ │ │ ├── context-menu-separator.svelte
│ │ │ ├── context-menu-shortcut.svelte
│ │ │ ├── context-menu-sub-content.svelte
│ │ │ ├── context-menu-sub-trigger.svelte
│ │ │ ├── context-menu-trigger.svelte
│ │ │ └── index.ts
│ │ │ ├── data-table
│ │ │ ├── data-table.svelte.ts
│ │ │ ├── flex-render.svelte
│ │ │ ├── index.ts
│ │ │ └── render-helpers.ts
│ │ │ ├── dialog
│ │ │ ├── dialog-close.svelte
│ │ │ ├── dialog-content.svelte
│ │ │ ├── dialog-description.svelte
│ │ │ ├── dialog-footer.svelte
│ │ │ ├── dialog-header.svelte
│ │ │ ├── dialog-overlay.svelte
│ │ │ ├── dialog-portal.svelte
│ │ │ ├── dialog-title.svelte
│ │ │ ├── dialog-trigger.svelte
│ │ │ └── index.ts
│ │ │ ├── drawer
│ │ │ ├── drawer-close.svelte
│ │ │ ├── drawer-content.svelte
│ │ │ ├── drawer-description.svelte
│ │ │ ├── drawer-footer.svelte
│ │ │ ├── drawer-header.svelte
│ │ │ ├── drawer-nested.svelte
│ │ │ ├── drawer-overlay.svelte
│ │ │ ├── drawer-title.svelte
│ │ │ ├── drawer-trigger.svelte
│ │ │ ├── drawer.svelte
│ │ │ └── index.ts
│ │ │ ├── dropdown-menu
│ │ │ ├── dropdown-menu-checkbox-item.svelte
│ │ │ ├── dropdown-menu-content.svelte
│ │ │ ├── dropdown-menu-group-heading.svelte
│ │ │ ├── dropdown-menu-group.svelte
│ │ │ ├── dropdown-menu-item.svelte
│ │ │ ├── dropdown-menu-label.svelte
│ │ │ ├── dropdown-menu-radio-group.svelte
│ │ │ ├── dropdown-menu-radio-item.svelte
│ │ │ ├── dropdown-menu-separator.svelte
│ │ │ ├── dropdown-menu-shortcut.svelte
│ │ │ ├── dropdown-menu-sub-content.svelte
│ │ │ ├── dropdown-menu-sub-trigger.svelte
│ │ │ ├── dropdown-menu-trigger.svelte
│ │ │ └── index.ts
│ │ │ ├── form
│ │ │ ├── form-button.svelte
│ │ │ ├── form-checkbox.svelte
│ │ │ ├── form-description.svelte
│ │ │ ├── form-element-field.svelte
│ │ │ ├── form-field-errors.svelte
│ │ │ ├── form-field.svelte
│ │ │ ├── form-fieldset.svelte
│ │ │ ├── form-input.svelte
│ │ │ ├── form-item.svelte
│ │ │ ├── form-label.svelte
│ │ │ ├── form-legend.svelte
│ │ │ ├── form-native-select.svelte
│ │ │ ├── form-radio-group.svelte
│ │ │ ├── form-select-trigger.svelte
│ │ │ ├── form-select.svelte
│ │ │ ├── form-switch.svelte
│ │ │ ├── form-textarea.svelte
│ │ │ ├── form-validation.svelte
│ │ │ └── index.ts
│ │ │ ├── hover-card
│ │ │ ├── hover-card-content.svelte
│ │ │ ├── hover-card-trigger.svelte
│ │ │ └── index.ts
│ │ │ ├── input-otp
│ │ │ ├── index.ts
│ │ │ ├── input-otp-group.svelte
│ │ │ ├── input-otp-separator.svelte
│ │ │ ├── input-otp-slot.svelte
│ │ │ └── input-otp.svelte
│ │ │ ├── input
│ │ │ ├── index.ts
│ │ │ └── input.svelte
│ │ │ ├── label
│ │ │ ├── index.ts
│ │ │ └── label.svelte
│ │ │ ├── menubar
│ │ │ ├── index.ts
│ │ │ ├── menubar-checkbox-item.svelte
│ │ │ ├── menubar-content.svelte
│ │ │ ├── menubar-group-heading.svelte
│ │ │ ├── menubar-group.svelte
│ │ │ ├── menubar-item.svelte
│ │ │ ├── menubar-label.svelte
│ │ │ ├── menubar-radio-item.svelte
│ │ │ ├── menubar-separator.svelte
│ │ │ ├── menubar-shortcut.svelte
│ │ │ ├── menubar-sub-content.svelte
│ │ │ ├── menubar-sub-trigger.svelte
│ │ │ ├── menubar-trigger.svelte
│ │ │ └── menubar.svelte
│ │ │ ├── pagination
│ │ │ ├── index.ts
│ │ │ ├── pagination-content.svelte
│ │ │ ├── pagination-ellipsis.svelte
│ │ │ ├── pagination-item.svelte
│ │ │ ├── pagination-link.svelte
│ │ │ ├── pagination-next-button.svelte
│ │ │ ├── pagination-prev-button.svelte
│ │ │ └── pagination.svelte
│ │ │ ├── popover
│ │ │ ├── index.ts
│ │ │ ├── popover-content.svelte
│ │ │ └── popover-trigger.svelte
│ │ │ ├── progress
│ │ │ ├── index.ts
│ │ │ └── progress.svelte
│ │ │ ├── radio-group
│ │ │ ├── index.ts
│ │ │ ├── radio-group-item.svelte
│ │ │ └── radio-group.svelte
│ │ │ ├── range-calendar
│ │ │ ├── index.ts
│ │ │ ├── range-calendar-cell.svelte
│ │ │ ├── range-calendar-day.svelte
│ │ │ ├── range-calendar-grid-row.svelte
│ │ │ ├── range-calendar-grid.svelte
│ │ │ ├── range-calendar-head-cell.svelte
│ │ │ ├── range-calendar-header.svelte
│ │ │ ├── range-calendar-heading.svelte
│ │ │ ├── range-calendar-months.svelte
│ │ │ ├── range-calendar-next-button.svelte
│ │ │ ├── range-calendar-prev-button.svelte
│ │ │ └── range-calendar.svelte
│ │ │ ├── resizable
│ │ │ ├── index.ts
│ │ │ ├── resizable-handle.svelte
│ │ │ └── resizable-pane-group.svelte
│ │ │ ├── scroll-area
│ │ │ ├── index.ts
│ │ │ ├── scroll-area-scrollbar.svelte
│ │ │ └── scroll-area.svelte
│ │ │ ├── select
│ │ │ ├── index.ts
│ │ │ ├── select-content.svelte
│ │ │ ├── select-group.svelte
│ │ │ ├── select-item.svelte
│ │ │ ├── select-label.svelte
│ │ │ ├── select-scroll-down-button.svelte
│ │ │ ├── select-scroll-up-button.svelte
│ │ │ ├── select-separator.svelte
│ │ │ └── select-trigger.svelte
│ │ │ ├── separator
│ │ │ ├── index.ts
│ │ │ └── separator.svelte
│ │ │ ├── sheet
│ │ │ ├── index.ts
│ │ │ ├── sheet-close.svelte
│ │ │ ├── sheet-content.svelte
│ │ │ ├── sheet-description.svelte
│ │ │ ├── sheet-footer.svelte
│ │ │ ├── sheet-header.svelte
│ │ │ ├── sheet-overlay.svelte
│ │ │ ├── sheet-title.svelte
│ │ │ └── sheet-trigger.svelte
│ │ │ ├── sidebar
│ │ │ ├── constants.ts
│ │ │ ├── context.svelte.ts
│ │ │ ├── index.ts
│ │ │ ├── sidebar-content.svelte
│ │ │ ├── sidebar-footer.svelte
│ │ │ ├── sidebar-group-action.svelte
│ │ │ ├── sidebar-group-content.svelte
│ │ │ ├── sidebar-group-label.svelte
│ │ │ ├── sidebar-group.svelte
│ │ │ ├── sidebar-header.svelte
│ │ │ ├── sidebar-input.svelte
│ │ │ ├── sidebar-inset.svelte
│ │ │ ├── sidebar-menu-action.svelte
│ │ │ ├── sidebar-menu-badge.svelte
│ │ │ ├── sidebar-menu-button.svelte
│ │ │ ├── sidebar-menu-item.svelte
│ │ │ ├── sidebar-menu-skeleton.svelte
│ │ │ ├── sidebar-menu-sub-button.svelte
│ │ │ ├── sidebar-menu-sub-item.svelte
│ │ │ ├── sidebar-menu-sub.svelte
│ │ │ ├── sidebar-menu.svelte
│ │ │ ├── sidebar-provider.svelte
│ │ │ ├── sidebar-rail.svelte
│ │ │ ├── sidebar-separator.svelte
│ │ │ ├── sidebar-trigger.svelte
│ │ │ └── sidebar.svelte
│ │ │ ├── skeleton
│ │ │ ├── index.ts
│ │ │ └── skeleton.svelte
│ │ │ ├── slider
│ │ │ ├── index.ts
│ │ │ └── slider.svelte
│ │ │ ├── sonner
│ │ │ ├── index.ts
│ │ │ └── sonner.svelte
│ │ │ ├── switch
│ │ │ ├── index.ts
│ │ │ └── switch.svelte
│ │ │ ├── table
│ │ │ ├── index.ts
│ │ │ ├── table-body.svelte
│ │ │ ├── table-caption.svelte
│ │ │ ├── table-cell.svelte
│ │ │ ├── table-footer.svelte
│ │ │ ├── table-head.svelte
│ │ │ ├── table-header.svelte
│ │ │ ├── table-row.svelte
│ │ │ └── table.svelte
│ │ │ ├── tabs
│ │ │ ├── index.ts
│ │ │ ├── tabs-content.svelte
│ │ │ ├── tabs-list.svelte
│ │ │ ├── tabs-trigger.svelte
│ │ │ └── tabs.svelte
│ │ │ ├── textarea
│ │ │ ├── index.ts
│ │ │ └── textarea.svelte
│ │ │ ├── toggle-group
│ │ │ ├── index.ts
│ │ │ ├── toggle-group-item.svelte
│ │ │ └── toggle-group.svelte
│ │ │ ├── toggle
│ │ │ ├── index.ts
│ │ │ └── toggle.svelte
│ │ │ └── tooltip
│ │ │ ├── index.ts
│ │ │ ├── tooltip-content.svelte
│ │ │ └── tooltip-trigger.svelte
│ ├── db
│ │ ├── clear.ts
│ │ ├── factories
│ │ │ ├── teamFactory.ts
│ │ │ ├── userFactory.ts
│ │ │ └── usersTeamsFactory.ts
│ │ ├── migrations
│ │ │ ├── 0000_lame_jane_foster.sql
│ │ │ └── meta
│ │ │ │ ├── 0000_snapshot.json
│ │ │ │ └── _journal.json
│ │ ├── models
│ │ │ ├── invite.ts
│ │ │ ├── session.ts
│ │ │ ├── team.ts
│ │ │ ├── token.ts
│ │ │ └── user.ts
│ │ ├── queries
│ │ │ ├── invite.ts
│ │ │ └── team.ts
│ │ └── seed.ts
│ ├── hooks
│ │ └── is-mobile.svelte.ts
│ ├── server
│ │ ├── auth.ts
│ │ ├── database.ts
│ │ ├── oauth.ts
│ │ └── storage.ts
│ ├── utils
│ │ ├── helpers
│ │ │ ├── forms.ts
│ │ │ ├── generate.ts
│ │ │ ├── password.ts
│ │ │ └── uploadFile.svelte.ts
│ │ ├── mail
│ │ │ ├── mailer.ts
│ │ │ ├── templates
│ │ │ │ ├── ResetPassword.svelte
│ │ │ │ ├── TeamInvite.svelte
│ │ │ │ ├── Welcome.svelte
│ │ │ │ └── mailTheme.ts
│ │ │ └── types.ts
│ │ ├── messages.json
│ │ ├── themes.ts
│ │ └── utils.ts
│ └── validations
│ │ ├── auth.ts
│ │ ├── files.ts
│ │ └── team.ts
├── routes
│ ├── (auth)
│ │ ├── +layout.svelte
│ │ ├── login
│ │ │ ├── +page.server.ts
│ │ │ ├── +page.svelte
│ │ │ └── google
│ │ │ │ ├── +server.ts
│ │ │ │ └── callback
│ │ │ │ └── +server.ts
│ │ ├── logout
│ │ │ └── +server.ts
│ │ ├── register
│ │ │ ├── +page.server.ts
│ │ │ └── +page.svelte
│ │ └── reset-password
│ │ │ ├── +page.server.ts
│ │ │ ├── +page.svelte
│ │ │ └── update
│ │ │ ├── +page.server.ts
│ │ │ └── +page.svelte
│ ├── (dashboard)
│ │ ├── +layout.server.ts
│ │ ├── +layout.svelte
│ │ ├── dashboard
│ │ │ └── +page.svelte
│ │ └── settings
│ │ │ ├── +layout.svelte
│ │ │ ├── profile
│ │ │ ├── +page.server.ts
│ │ │ └── +page.svelte
│ │ │ └── teams
│ │ │ ├── +page.server.ts
│ │ │ ├── +page.svelte
│ │ │ └── [teamPublicId]
│ │ │ ├── +page.server.ts
│ │ │ └── +page.svelte
│ ├── +error.svelte
│ ├── +layout.server.ts
│ ├── +layout.svelte
│ ├── +page.server.ts
│ ├── +page.svelte
│ └── api
│ │ ├── invites
│ │ └── +server.ts
│ │ └── upload
│ │ └── +server.ts
└── styles
│ └── app.css
├── static
├── favicon.png
├── fonts
│ ├── Montserrat-VariableFont.ttf
│ └── Sora-VariableFont.ttf
└── robots.txt
├── svelte.config.js
├── tests
├── e2e
│ └── homepage.test.ts
└── unit
│ └── demo.spec.ts
├── tsconfig.json
├── vite.config.ts
└── vitest-setup-client.ts
/.env.example:
--------------------------------------------------------------------------------
1 | # General
2 | PUBLIC_BASE_URL="http://localhost:5173"
3 |
4 | # Database (LOCAL)
5 | LOCAL_DATABASE_URL="file:sveltekit_omakase_local.db"
6 |
7 | # Database (PRODUCTION)
8 | DATABASE_URL="libsql://db-name.turso.io"
9 | DATABASE_AUTH_TOKEN=""
10 |
11 | # Google
12 | GOOGLE_CLIENT_ID=""
13 | GOOGLE_CLIENT_SECRET=""
14 |
15 | # R2
16 | R2_ACCOUNT_ID=""
17 | R2_ACCESS_KEY_ID=""
18 | R2_SECRET_ACCESS_KEY=""
19 | PUBLIC_R2_BUCKET_NAME=""
20 | PUBLIC_R2_BUCKET_URL="" # use R2.dev subdomain in development
21 |
22 | # Resend
23 | RESEND_API_KEY=""
24 | EMAIL_SENDER=""
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Lint & Test
2 |
3 | on:
4 | push:
5 | branches: ['main']
6 | pull_request:
7 | branches: ['main']
8 |
9 | jobs:
10 | lint-and-test:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - uses: actions/checkout@v4
15 |
16 | # Set up pnpm
17 | - name: Set up pnpm
18 | uses: pnpm/action-setup@v4
19 | with:
20 | version: 9 # Specify pnpm version if needed, otherwise latest
21 |
22 | # Set up Node.js
23 | - name: Set up Node.js
24 | uses: actions/setup-node@v4
25 | with:
26 | node-version: 'lts/*' # Using the latest Node.js LTS version
27 | cache: 'pnpm'
28 |
29 | # Install dependencies
30 | - name: Install dependencies
31 | run: pnpm install --frozen-lockfile
32 |
33 | # Run linter
34 | - name: Run linter
35 | run: pnpm lint
36 |
37 | # Run unit tests
38 | - name: Run unit tests
39 | run: pnpm test:unit
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /package
6 | .env
7 | .env.*
8 | !.env.example
9 | dev.db
10 | /test-results
11 | /.vscode
12 | *.db
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 | resolution-mode=highest
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /package
6 | .env
7 | .env.*
8 | !.env.example
9 | /src/lib/db/migrations
10 |
11 | # Ignore files for PNPM, NPM and YARN
12 | pnpm-lock.yaml
13 | package-lock.json
14 | yarn.lock
15 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": false,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "printWidth": 120,
6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
7 | "overrides": [
8 | {
9 | "files": "*.svelte",
10 | "options": { "parser": "svelte" }
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://next.shadcn-svelte.com/schema.json",
3 | "style": "new-york",
4 | "tailwind": {
5 | "config": "tailwind.config.js",
6 | "css": "src/styles/app.css",
7 | "baseColor": "slate"
8 | },
9 | "aliases": {
10 | "components": "$components",
11 | "utils": "$lib/utils/utils",
12 | "ui": "$lib/components/ui",
13 | "hooks": "$lib/hooks"
14 | },
15 | "typescript": true,
16 | "registry": "https://next.shadcn-svelte.com/registry"
17 | }
18 |
--------------------------------------------------------------------------------
/drizzle.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from 'drizzle-kit';
2 | import 'dotenv/config';
3 |
4 | const localConfig = {
5 | dialect: 'turso',
6 | schema: './src/lib/db/models/*',
7 | out: './src/lib/db/migrations',
8 | breakpoints: true,
9 | casing: 'snake_case',
10 | verbose: true,
11 | dbCredentials: {
12 | url: process.env.LOCAL_DATABASE_URL || ''
13 | }
14 | } as Config;
15 |
16 | const remoteConfig = {
17 | dialect: 'turso',
18 | schema: './src/lib/db/models/*',
19 | out: './src/lib/db/migrations',
20 | breakpoints: true,
21 | casing: 'snake_case',
22 | verbose: true,
23 | dbCredentials: {
24 | url: process.env.DATABASE_URL || '',
25 | authToken: process.env.DATABASE_AUTH_TOKEN || ''
26 | }
27 | } as Config;
28 |
29 | export default process.env.NODE_ENV === 'production' ? remoteConfig : localConfig;
30 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import prettier from 'eslint-config-prettier';
2 | import js from '@eslint/js';
3 | import { includeIgnoreFile } from '@eslint/compat';
4 | import svelte from 'eslint-plugin-svelte';
5 | import globals from 'globals';
6 | import { fileURLToPath } from 'node:url';
7 | import ts from 'typescript-eslint';
8 | import svelteConfig from './svelte.config.js';
9 |
10 | const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
11 |
12 | export default ts.config(
13 | includeIgnoreFile(gitignorePath),
14 | js.configs.recommended,
15 | ...ts.configs.recommended,
16 | ...svelte.configs.recommended,
17 | prettier,
18 | ...svelte.configs.prettier,
19 | {
20 | languageOptions: {
21 | globals: { ...globals.browser, ...globals.node }
22 | },
23 | rules: { 'no-undef': 'off' }
24 | },
25 | {
26 | files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
27 | languageOptions: {
28 | parserOptions: {
29 | projectService: true,
30 | extraFileExtensions: ['.svelte'],
31 | parser: ts.parser,
32 | svelteConfig
33 | }
34 | }
35 | },
36 | {
37 | ignores: ['*.env', 'node_modules/', 'build/', '.svelte-kit/', 'src/lib/components/ui/**']
38 | }
39 | );
40 |
--------------------------------------------------------------------------------
/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '@playwright/test';
2 |
3 | export default defineConfig({
4 | webServer: {
5 | command: 'pnpm build && pnpm preview',
6 | port: 4173
7 | },
8 | testDir: 'tests/e2e'
9 | });
10 |
--------------------------------------------------------------------------------
/src/app.d.ts:
--------------------------------------------------------------------------------
1 | declare global {
2 | namespace App {
3 | interface Locals {
4 | user: import('$lib/server/auth').SessionValidationResult['user'];
5 | session: import('$lib/server/auth').SessionValidationResult['session'];
6 | }
7 |
8 | interface PageData {
9 | metadata: {
10 | title: string;
11 | description: string;
12 | image: string;
13 | url: string;
14 | breadcrumbs: {
15 | title: string;
16 | href: string;
17 | }[];
18 | };
19 | }
20 | }
21 | }
22 |
23 | export {};
24 |
--------------------------------------------------------------------------------
/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
17 | %sveltekit.head%
18 |
19 |
20 | %sveltekit.body%
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/hooks.server.ts:
--------------------------------------------------------------------------------
1 | import type { Handle } from '@sveltejs/kit';
2 | import * as auth from '$lib/server/auth';
3 |
4 | const handleAuth: Handle = async ({ event, resolve }) => {
5 | const sessionToken = event.cookies.get(auth.sessionCookieName);
6 | if (!sessionToken) {
7 | event.locals.user = null;
8 | event.locals.session = null;
9 | return resolve(event);
10 | }
11 |
12 | const { session, user } = await auth.validateSessionToken(sessionToken);
13 | if (session) {
14 | auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
15 | } else {
16 | auth.deleteSessionTokenCookie(event);
17 | }
18 |
19 | event.locals.user = user;
20 | event.locals.session = session;
21 |
22 | return resolve(event);
23 | };
24 |
25 | export const handle: Handle = handleAuth;
26 |
--------------------------------------------------------------------------------
/src/lib/assets/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n00ki/sveltekit-omakase/8de5357d79ffdc52acae042c0731f3b6fda97135/src/lib/assets/avatar.png
--------------------------------------------------------------------------------
/src/lib/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n00ki/sveltekit-omakase/8de5357d79ffdc52acae042c0731f3b6fda97135/src/lib/assets/logo.png
--------------------------------------------------------------------------------
/src/lib/assets/meta_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n00ki/sveltekit-omakase/8de5357d79ffdc52acae042c0731f3b6fda97135/src/lib/assets/meta_image.png
--------------------------------------------------------------------------------
/src/lib/components/PlaceholderPattern.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
18 |
--------------------------------------------------------------------------------
/src/lib/components/SidebarHeader.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 | {#snippet child({ props })}
13 |
14 |
19 |
20 | SvelteKit Omakase
21 |
22 |
23 | {/snippet}
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/lib/components/Wrapper.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 | {@render children?.()}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/accordion/accordion-content.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
22 |
23 | {@render children?.()}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/lib/components/ui/accordion/accordion-item.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/accordion/accordion-root.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/accordion/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './accordion-root.svelte';
2 | import Content from './accordion-content.svelte';
3 | import Item from './accordion-item.svelte';
4 | import Trigger from './accordion-trigger.svelte';
5 |
6 | export {
7 | Root,
8 | Content,
9 | Item,
10 | Trigger,
11 | //
12 | Root as Accordion,
13 | Content as AccordionContent,
14 | Item as AccordionItem,
15 | Trigger as AccordionTrigger
16 | };
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
15 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
15 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
27 |
28 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert-dialog/index.ts:
--------------------------------------------------------------------------------
1 | import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
2 | import Trigger from './alert-dialog-trigger.svelte';
3 | import Title from './alert-dialog-title.svelte';
4 | import Action from './alert-dialog-action.svelte';
5 | import Cancel from './alert-dialog-cancel.svelte';
6 | import Footer from './alert-dialog-footer.svelte';
7 | import Header from './alert-dialog-header.svelte';
8 | import Overlay from './alert-dialog-overlay.svelte';
9 | import Content from './alert-dialog-content.svelte';
10 | import Description from './alert-dialog-description.svelte';
11 |
12 | const Root = AlertDialogPrimitive.Root;
13 | const Portal = AlertDialogPrimitive.Portal;
14 |
15 | export {
16 | Root,
17 | Title,
18 | Action,
19 | Cancel,
20 | Portal,
21 | Footer,
22 | Header,
23 | Trigger,
24 | Overlay,
25 | Content,
26 | Description,
27 | //
28 | Root as AlertDialog,
29 | Title as AlertDialogTitle,
30 | Action as AlertDialogAction,
31 | Cancel as AlertDialogCancel,
32 | Portal as AlertDialogPortal,
33 | Footer as AlertDialogFooter,
34 | Header as AlertDialogHeader,
35 | Trigger as AlertDialogTrigger,
36 | Overlay as AlertDialogOverlay,
37 | Content as AlertDialogContent,
38 | Description as AlertDialogDescription
39 | };
40 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert/alert-description.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
22 | {@render children?.()}
23 |
24 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert/alert-title.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/alert/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './alert.svelte';
2 | import Description from './alert-description.svelte';
3 | import Title from './alert-title.svelte';
4 | export { alertVariants, type AlertVariant } from './alert.svelte';
5 |
6 | export {
7 | Root,
8 | Description,
9 | Title,
10 | //
11 | Root as Alert,
12 | Description as AlertDescription,
13 | Title as AlertTitle
14 | };
15 |
--------------------------------------------------------------------------------
/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/aspect-ratio/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './aspect-ratio.svelte';
2 |
3 | export { Root, Root as AspectRatio };
4 |
--------------------------------------------------------------------------------
/src/lib/components/ui/avatar/avatar-fallback.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/avatar/avatar-image.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/avatar/avatar.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/avatar/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './avatar.svelte';
2 | import Image from './avatar-image.svelte';
3 | import Fallback from './avatar-fallback.svelte';
4 |
5 | export {
6 | Root,
7 | Image,
8 | Fallback,
9 | //
10 | Root as Avatar,
11 | Image as AvatarImage,
12 | Fallback as AvatarFallback
13 | };
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/badge/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Badge } from './badge.svelte';
2 | export { badgeVariants, type BadgeVariant } from './badge.svelte';
3 |
--------------------------------------------------------------------------------
/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
21 |
22 | More
23 |
24 |
--------------------------------------------------------------------------------
/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte:
--------------------------------------------------------------------------------
1 |
24 |
25 | {#if child}
26 | {@render child({ props: attrs })}
27 | {:else}
28 |
29 | {@render children?.()}
30 |
31 | {/if}
32 |
--------------------------------------------------------------------------------
/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
22 | {@render children?.()}
23 |
24 |
--------------------------------------------------------------------------------
/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 | svg]:size-3.5', className)}
15 | {...restProps}
16 | >
17 | {#if children}
18 | {@render children?.()}
19 | {:else}
20 |
21 | {/if}
22 |
23 |
--------------------------------------------------------------------------------
/src/lib/components/ui/breadcrumb/breadcrumb.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/breadcrumb/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './breadcrumb.svelte';
2 | import Ellipsis from './breadcrumb-ellipsis.svelte';
3 | import Item from './breadcrumb-item.svelte';
4 | import Separator from './breadcrumb-separator.svelte';
5 | import Link from './breadcrumb-link.svelte';
6 | import List from './breadcrumb-list.svelte';
7 | import Page from './breadcrumb-page.svelte';
8 |
9 | export {
10 | Root,
11 | Ellipsis,
12 | Item,
13 | Separator,
14 | Link,
15 | List,
16 | Page,
17 | //
18 | Root as Breadcrumb,
19 | Ellipsis as BreadcrumbEllipsis,
20 | Item as BreadcrumbItem,
21 | Separator as BreadcrumbSeparator,
22 | Link as BreadcrumbLink,
23 | List as BreadcrumbList,
24 | Page as BreadcrumbPage
25 | };
26 |
--------------------------------------------------------------------------------
/src/lib/components/ui/button/index.ts:
--------------------------------------------------------------------------------
1 | import Root, { type ButtonProps, type ButtonSize, type ButtonVariant, buttonVariants } from './button.svelte';
2 |
3 | export {
4 | Root,
5 | type ButtonProps as Props,
6 | //
7 | Root as Button,
8 | buttonVariants,
9 | type ButtonProps,
10 | type ButtonSize,
11 | type ButtonVariant
12 | };
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-cell.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-grid-body.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-grid-head.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-grid-row.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-grid.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-head-cell.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-header.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-heading.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-months.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
18 | {@render children?.()}
19 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-next-button.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 | {#snippet Fallback()}
11 |
12 | {/snippet}
13 |
14 |
24 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-prev-button.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 | {#snippet Fallback()}
11 |
12 | {/snippet}
13 |
14 |
24 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './calendar.svelte';
2 | import Cell from './calendar-cell.svelte';
3 | import Day from './calendar-day.svelte';
4 | import Grid from './calendar-grid.svelte';
5 | import Header from './calendar-header.svelte';
6 | import Months from './calendar-months.svelte';
7 | import GridRow from './calendar-grid-row.svelte';
8 | import Heading from './calendar-heading.svelte';
9 | import GridBody from './calendar-grid-body.svelte';
10 | import GridHead from './calendar-grid-head.svelte';
11 | import HeadCell from './calendar-head-cell.svelte';
12 | import NextButton from './calendar-next-button.svelte';
13 | import PrevButton from './calendar-prev-button.svelte';
14 |
15 | export {
16 | Day,
17 | Cell,
18 | Grid,
19 | Header,
20 | Months,
21 | GridRow,
22 | Heading,
23 | GridBody,
24 | GridHead,
25 | HeadCell,
26 | NextButton,
27 | PrevButton,
28 | //
29 | Root as Calendar
30 | };
31 |
--------------------------------------------------------------------------------
/src/lib/components/ui/card/card-action.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/card/card-content.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/card/card-description.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/card/card-footer.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/card/card-header.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
22 | {@render children?.()}
23 |
24 |
--------------------------------------------------------------------------------
/src/lib/components/ui/card/card-title.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/card/card.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/card/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './card.svelte';
2 | import Content from './card-content.svelte';
3 | import Description from './card-description.svelte';
4 | import Footer from './card-footer.svelte';
5 | import Header from './card-header.svelte';
6 | import Title from './card-title.svelte';
7 | import Action from './card-action.svelte';
8 |
9 | export {
10 | Root,
11 | Content,
12 | Description,
13 | Footer,
14 | Header,
15 | Title,
16 | Action,
17 | //
18 | Root as Card,
19 | Content as CardContent,
20 | Description as CardDescription,
21 | Footer as CardFooter,
22 | Header as CardHeader,
23 | Title as CardTitle,
24 | Action as CardAction
25 | };
26 |
--------------------------------------------------------------------------------
/src/lib/components/ui/carousel/carousel-content.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
31 |
37 | {@render children?.()}
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/lib/components/ui/carousel/carousel-item.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
25 | {@render children?.()}
26 |
27 |
--------------------------------------------------------------------------------
/src/lib/components/ui/carousel/carousel-next.svelte:
--------------------------------------------------------------------------------
1 |
18 |
19 |
39 |
--------------------------------------------------------------------------------
/src/lib/components/ui/carousel/carousel-previous.svelte:
--------------------------------------------------------------------------------
1 |
18 |
19 |
39 |
--------------------------------------------------------------------------------
/src/lib/components/ui/carousel/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './carousel.svelte';
2 | import Content from './carousel-content.svelte';
3 | import Item from './carousel-item.svelte';
4 | import Previous from './carousel-previous.svelte';
5 | import Next from './carousel-next.svelte';
6 |
7 | export {
8 | Root,
9 | Content,
10 | Item,
11 | Previous,
12 | Next,
13 | //
14 | Root as Carousel,
15 | Content as CarouselContent,
16 | Item as CarouselItem,
17 | Previous as CarouselPrevious,
18 | Next as CarouselNext
19 | };
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/chart/chart-style.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 | {#if colorConfig && colorConfig.length}
15 | {@const themeContents = Object.entries(THEMES)
16 | .map(
17 | ([theme, prefix]) => `
18 | ${prefix} [data-chart=${id}] {
19 | ${colorConfig
20 | .map(([key, itemConfig]) => {
21 | const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;
22 | return color ? ` --color-${key}: ${color};` : null;
23 | })
24 | .join('\n')}
25 | }
26 | `
27 | )
28 | .join('\n')}
29 |
30 | {#key id}
31 |
32 | {@html `${styleOpen}
33 | ${themeContents}
34 | ${styleClose}`}
35 | {/key}
36 | {/if}
37 |
--------------------------------------------------------------------------------
/src/lib/components/ui/chart/index.ts:
--------------------------------------------------------------------------------
1 | import ChartContainer from './chart-container.svelte';
2 | import ChartTooltip from './chart-tooltip.svelte';
3 |
4 | export { getPayloadConfigFromPayload, type ChartConfig } from './chart-utils.js';
5 |
6 | export { ChartContainer, ChartTooltip, ChartContainer as Container, ChartTooltip as Tooltip };
7 |
--------------------------------------------------------------------------------
/src/lib/components/ui/checkbox/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './checkbox.svelte';
2 | export {
3 | Root,
4 | //
5 | Root as Checkbox
6 | };
7 |
--------------------------------------------------------------------------------
/src/lib/components/ui/collapsible/collapsible-content.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/collapsible/collapsible-trigger.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/collapsible/collapsible.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/collapsible/index.ts:
--------------------------------------------------------------------------------
1 | import { Collapsible as CollapsiblePrimitive } from 'bits-ui';
2 |
3 | const Root = CollapsiblePrimitive.Root;
4 | const Trigger = CollapsiblePrimitive.Trigger;
5 | const Content = CollapsiblePrimitive.Content;
6 |
7 | export {
8 | Root,
9 | Content,
10 | Trigger,
11 | //
12 | Root as Collapsible,
13 | Content as CollapsibleContent,
14 | Trigger as CollapsibleTrigger
15 | };
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/command/command-empty.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/command/command-group.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
24 | {#if heading}
25 |
26 | {heading}
27 |
28 | {/if}
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/lib/components/ui/command/command-input.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
26 |
27 |
--------------------------------------------------------------------------------
/src/lib/components/ui/command/command-item.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/command/command-link-item.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/command/command-list.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/command/command-separator.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/command/command-shortcut.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/command/command.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/command/index.ts:
--------------------------------------------------------------------------------
1 | import { Command as CommandPrimitive } from 'bits-ui';
2 |
3 | import Root from './command.svelte';
4 | import Dialog from './command-dialog.svelte';
5 | import Empty from './command-empty.svelte';
6 | import Group from './command-group.svelte';
7 | import Item from './command-item.svelte';
8 | import Input from './command-input.svelte';
9 | import List from './command-list.svelte';
10 | import Separator from './command-separator.svelte';
11 | import Shortcut from './command-shortcut.svelte';
12 | import LinkItem from './command-link-item.svelte';
13 |
14 | const Loading = CommandPrimitive.Loading;
15 |
16 | export {
17 | Root,
18 | Dialog,
19 | Empty,
20 | Group,
21 | Item,
22 | LinkItem,
23 | Input,
24 | List,
25 | Separator,
26 | Shortcut,
27 | Loading,
28 | //
29 | Root as Command,
30 | Dialog as CommandDialog,
31 | Empty as CommandEmpty,
32 | Group as CommandGroup,
33 | Item as CommandItem,
34 | LinkItem as CommandLinkItem,
35 | Input as CommandInput,
36 | List as CommandList,
37 | Separator as CommandSeparator,
38 | Shortcut as CommandShortcut,
39 | Loading as CommandLoading
40 | };
41 |
--------------------------------------------------------------------------------
/src/lib/components/ui/context-menu/context-menu-content.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
25 |
26 |
--------------------------------------------------------------------------------
/src/lib/components/ui/context-menu/context-menu-group-heading.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/context-menu/context-menu-group.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/context-menu/context-menu-label.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
23 | {@render children?.()}
24 |
25 |
--------------------------------------------------------------------------------
/src/lib/components/ui/context-menu/context-menu-radio-group.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/context-menu/context-menu-separator.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/context-menu/context-menu-shortcut.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/context-menu/context-menu-sub-content.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
27 | {@render children?.()}
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/lib/components/ui/context-menu/context-menu-trigger.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/data-table/index.ts:
--------------------------------------------------------------------------------
1 | export { default as FlexRender } from './flex-render.svelte';
2 | export { renderComponent, renderSnippet } from './render-helpers.js';
3 | export { createSvelteTable } from './data-table.svelte.js';
4 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dialog/dialog-close.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dialog/dialog-description.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dialog/dialog-footer.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dialog/dialog-header.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dialog/dialog-overlay.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dialog/dialog-portal.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dialog/dialog-title.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dialog/dialog-trigger.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dialog/index.ts:
--------------------------------------------------------------------------------
1 | import { Dialog as DialogPrimitive } from 'bits-ui';
2 |
3 | import Title from './dialog-title.svelte';
4 | import Footer from './dialog-footer.svelte';
5 | import Header from './dialog-header.svelte';
6 | import Overlay from './dialog-overlay.svelte';
7 | import Content from './dialog-content.svelte';
8 | import Description from './dialog-description.svelte';
9 | import Trigger from './dialog-trigger.svelte';
10 | import Close from './dialog-close.svelte';
11 |
12 | const Root = DialogPrimitive.Root;
13 | const Portal = DialogPrimitive.Portal;
14 |
15 | export {
16 | Root,
17 | Title,
18 | Portal,
19 | Footer,
20 | Header,
21 | Trigger,
22 | Overlay,
23 | Content,
24 | Description,
25 | Close,
26 | //
27 | Root as Dialog,
28 | Title as DialogTitle,
29 | Portal as DialogPortal,
30 | Footer as DialogFooter,
31 | Header as DialogHeader,
32 | Trigger as DialogTrigger,
33 | Overlay as DialogOverlay,
34 | Content as DialogContent,
35 | Description as DialogDescription,
36 | Close as DialogClose
37 | };
38 |
--------------------------------------------------------------------------------
/src/lib/components/ui/drawer/drawer-close.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/drawer/drawer-description.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/drawer/drawer-footer.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/drawer/drawer-header.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/drawer/drawer-nested.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/drawer/drawer-overlay.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/drawer/drawer-title.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/drawer/drawer-trigger.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/drawer/drawer.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/drawer/index.ts:
--------------------------------------------------------------------------------
1 | import { Drawer as DrawerPrimitive } from 'vaul-svelte';
2 |
3 | import Root from './drawer.svelte';
4 | import Content from './drawer-content.svelte';
5 | import Description from './drawer-description.svelte';
6 | import Overlay from './drawer-overlay.svelte';
7 | import Footer from './drawer-footer.svelte';
8 | import Header from './drawer-header.svelte';
9 | import Title from './drawer-title.svelte';
10 | import NestedRoot from './drawer-nested.svelte';
11 | import Close from './drawer-close.svelte';
12 | import Trigger from './drawer-trigger.svelte';
13 |
14 | const Portal: typeof DrawerPrimitive.Portal = DrawerPrimitive.Portal;
15 |
16 | export {
17 | Root,
18 | NestedRoot,
19 | Content,
20 | Description,
21 | Overlay,
22 | Footer,
23 | Header,
24 | Title,
25 | Trigger,
26 | Portal,
27 | Close,
28 |
29 | //
30 | Root as Drawer,
31 | NestedRoot as DrawerNestedRoot,
32 | Content as DrawerContent,
33 | Description as DrawerDescription,
34 | Overlay as DrawerOverlay,
35 | Footer as DrawerFooter,
36 | Header as DrawerHeader,
37 | Title as DrawerTitle,
38 | Trigger as DrawerTrigger,
39 | Portal as DrawerPortal,
40 | Close as DrawerClose
41 | };
42 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
23 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
23 | {@render children?.()}
24 |
25 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
27 | {@render children?.()}
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-button.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-checkbox.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 | {
24 | onCheckedChange?.(v);
25 | setValue(v);
26 | }}
27 | {...rest_1}
28 | on:click
29 | on:keydown
30 | />
31 |
32 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-description.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-element-field.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
21 |
22 |
23 | {#snippet children({ constraints, errors, tainted, value })}
24 |
25 | {@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })}
26 |
27 | {/snippet}
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-field-errors.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 | {#snippet children({ errors, errorProps })}
18 | {#if childrenProp}
19 | {@render childrenProp({ errors, errorProps })}
20 | {:else}
21 | {#each errors as error (error)}
22 | {error}
23 | {/each}
24 | {/if}
25 | {/snippet}
26 |
27 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-field.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
21 |
22 |
23 | {#snippet children({ constraints, errors, tainted, value })}
24 |
25 | {@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })}
26 |
27 | {/snippet}
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-fieldset.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-input.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
34 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-item.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 | {@render children?.()}
17 |
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-label.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 | {#snippet child({ props })}
16 |
19 | {/snippet}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-legend.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-native-select.svelte:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
24 | {@render children?.()}
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-radio-group.svelte:
--------------------------------------------------------------------------------
1 |
17 |
18 | {
21 | onValueChange?.(v);
22 | setValue(v);
23 | }}
24 | {...rest}
25 | >
26 | {@render children?.()}
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-select-trigger.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 | {@render children?.()}
23 |
24 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-select.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 | {
19 | onSelectedChange?.(v);
20 | setValue(v ? v.value : undefined);
21 | }}
22 | {...rest}
23 | >
24 | {@render children?.()}
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-switch.svelte:
--------------------------------------------------------------------------------
1 |
17 |
18 | {
22 | onCheckedChange?.(v);
23 | setValue(v);
24 | }}
25 | {...rest}
26 | on:click
27 | on:keydown
28 | />
29 |
30 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-textarea.svelte:
--------------------------------------------------------------------------------
1 |
17 |
18 |
35 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-validation.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/index.ts:
--------------------------------------------------------------------------------
1 | import * as FormPrimitive from 'formsnap';
2 | import Description from './form-description.svelte';
3 | import Label from './form-label.svelte';
4 | import FieldErrors from './form-field-errors.svelte';
5 | import Field from './form-field.svelte';
6 | import Fieldset from './form-fieldset.svelte';
7 | import Legend from './form-legend.svelte';
8 | import ElementField from './form-element-field.svelte';
9 | import Button from './form-button.svelte';
10 |
11 | const Control = FormPrimitive.Control;
12 |
13 | export {
14 | Field,
15 | Control,
16 | Label,
17 | Button,
18 | FieldErrors,
19 | Description,
20 | Fieldset,
21 | Legend,
22 | ElementField,
23 | //
24 | Field as FormField,
25 | Control as FormControl,
26 | Description as FormDescription,
27 | Label as FormLabel,
28 | FieldErrors as FormFieldErrors,
29 | Fieldset as FormFieldset,
30 | Legend as FormLegend,
31 | ElementField as FormElementField,
32 | Button as FormButton
33 | };
34 |
--------------------------------------------------------------------------------
/src/lib/components/ui/hover-card/hover-card-content.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
29 |
30 |
--------------------------------------------------------------------------------
/src/lib/components/ui/hover-card/hover-card-trigger.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/hover-card/index.ts:
--------------------------------------------------------------------------------
1 | import { LinkPreview as HoverCardPrimitive } from 'bits-ui';
2 | import Content from './hover-card-content.svelte';
3 | import Trigger from './hover-card-trigger.svelte';
4 |
5 | const Root = HoverCardPrimitive.Root;
6 |
7 | export { Root, Content, Trigger, Root as HoverCard, Content as HoverCardContent, Trigger as HoverCardTrigger };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/input-otp/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './input-otp.svelte';
2 | import Group from './input-otp-group.svelte';
3 | import Slot from './input-otp-slot.svelte';
4 | import Separator from './input-otp-separator.svelte';
5 |
6 | export {
7 | Root,
8 | Group,
9 | Slot,
10 | Separator,
11 | Root as InputOTP,
12 | Group as InputOTPGroup,
13 | Slot as InputOTPSlot,
14 | Separator as InputOTPSeparator
15 | };
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/input-otp/input-otp-group.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/input-otp/input-otp-separator.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | {#if children}
11 | {@render children?.()}
12 | {:else}
13 |
14 | {/if}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/input-otp/input-otp-slot.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
20 | {cell.char}
21 | {#if cell.hasFakeCaret}
22 |
25 | {/if}
26 |
27 |
--------------------------------------------------------------------------------
/src/lib/components/ui/input-otp/input-otp.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/input/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './input.svelte';
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Input
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/label/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './label.svelte';
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Label
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/label/label.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar-group-heading.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
23 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar-group.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar-item.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
28 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar-label.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
24 | {@render children?.()}
25 |
26 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar-radio-item.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
23 | {#snippet children({ checked })}
24 |
25 | {#if checked}
26 |
27 | {/if}
28 |
29 | {@render childrenProp?.({ checked })}
30 | {/snippet}
31 |
32 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar-separator.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar-shortcut.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar-sub-content.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar-sub-trigger.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
27 | {@render children?.()}
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar-trigger.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/menubar/menubar.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/pagination/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './pagination.svelte';
2 | import Content from './pagination-content.svelte';
3 | import Item from './pagination-item.svelte';
4 | import Link from './pagination-link.svelte';
5 | import PrevButton from './pagination-prev-button.svelte';
6 | import NextButton from './pagination-next-button.svelte';
7 | import Ellipsis from './pagination-ellipsis.svelte';
8 |
9 | export {
10 | Root,
11 | Content,
12 | Item,
13 | Link,
14 | PrevButton,
15 | NextButton,
16 | Ellipsis,
17 | //
18 | Root as Pagination,
19 | Content as PaginationContent,
20 | Item as PaginationItem,
21 | Link as PaginationLink,
22 | PrevButton as PaginationPrevButton,
23 | NextButton as PaginationNextButton,
24 | Ellipsis as PaginationEllipsis
25 | };
26 |
--------------------------------------------------------------------------------
/src/lib/components/ui/pagination/pagination-content.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/pagination/pagination-ellipsis.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 |
21 | More pages
22 |
23 |
--------------------------------------------------------------------------------
/src/lib/components/ui/pagination/pagination-item.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 | {@render children?.()}
10 |
11 |
--------------------------------------------------------------------------------
/src/lib/components/ui/pagination/pagination-link.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 | {#snippet Fallback()}
21 | {page.value}
22 | {/snippet}
23 |
24 |
40 |
--------------------------------------------------------------------------------
/src/lib/components/ui/pagination/pagination-next-button.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 | {#snippet Fallback()}
16 | Next
17 |
18 | {/snippet}
19 |
20 |
34 |
--------------------------------------------------------------------------------
/src/lib/components/ui/pagination/pagination-prev-button.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 | {#snippet Fallback()}
16 |
17 | Previous
18 | {/snippet}
19 |
20 |
34 |
--------------------------------------------------------------------------------
/src/lib/components/ui/pagination/pagination.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/popover/index.ts:
--------------------------------------------------------------------------------
1 | import { Popover as PopoverPrimitive } from 'bits-ui';
2 | import Content from './popover-content.svelte';
3 | import Trigger from './popover-trigger.svelte';
4 | const Root = PopoverPrimitive.Root;
5 | const Close = PopoverPrimitive.Close;
6 |
7 | export {
8 | Root,
9 | Content,
10 | Trigger,
11 | Close,
12 | //
13 | Root as Popover,
14 | Content as PopoverContent,
15 | Trigger as PopoverTrigger,
16 | Close as PopoverClose
17 | };
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/popover/popover-content.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
29 |
30 |
--------------------------------------------------------------------------------
/src/lib/components/ui/popover/popover-trigger.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/progress/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './progress.svelte';
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Progress
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/progress/progress.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
22 |
27 |
28 |
--------------------------------------------------------------------------------
/src/lib/components/ui/radio-group/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './radio-group.svelte';
2 | import Item from './radio-group-item.svelte';
3 |
4 | export {
5 | Root,
6 | Item,
7 | //
8 | Root as RadioGroup,
9 | Item as RadioGroupItem
10 | };
11 |
--------------------------------------------------------------------------------
/src/lib/components/ui/radio-group/radio-group.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/range-calendar/index.ts:
--------------------------------------------------------------------------------
1 | import { RangeCalendar as RangeCalendarPrimitive } from 'bits-ui';
2 | import Root from './range-calendar.svelte';
3 | import Cell from './range-calendar-cell.svelte';
4 | import Day from './range-calendar-day.svelte';
5 | import Grid from './range-calendar-grid.svelte';
6 | import Header from './range-calendar-header.svelte';
7 | import Months from './range-calendar-months.svelte';
8 | import GridRow from './range-calendar-grid-row.svelte';
9 | import Heading from './range-calendar-heading.svelte';
10 | import HeadCell from './range-calendar-head-cell.svelte';
11 | import NextButton from './range-calendar-next-button.svelte';
12 | import PrevButton from './range-calendar-prev-button.svelte';
13 |
14 | const GridHead = RangeCalendarPrimitive.GridHead;
15 | const GridBody = RangeCalendarPrimitive.GridBody;
16 |
17 | export {
18 | Day,
19 | Cell,
20 | Grid,
21 | Header,
22 | Months,
23 | GridRow,
24 | Heading,
25 | GridBody,
26 | GridHead,
27 | HeadCell,
28 | NextButton,
29 | PrevButton,
30 | //
31 | Root as RangeCalendar
32 | };
33 |
--------------------------------------------------------------------------------
/src/lib/components/ui/range-calendar/range-calendar-cell.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/range-calendar/range-calendar-grid.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/range-calendar/range-calendar-header.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/range-calendar/range-calendar-heading.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/range-calendar/range-calendar-months.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
18 | {@render children?.()}
19 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 | {#snippet Fallback()}
16 |
17 | {/snippet}
18 |
19 |
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 | {#snippet Fallback()}
16 |
17 | {/snippet}
18 |
19 |
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/resizable/index.ts:
--------------------------------------------------------------------------------
1 | import { Pane } from 'paneforge';
2 | import Handle from './resizable-handle.svelte';
3 | import PaneGroup from './resizable-pane-group.svelte';
4 |
5 | export {
6 | PaneGroup,
7 | Pane,
8 | Handle,
9 | //
10 | PaneGroup as ResizablePaneGroup,
11 | Pane as ResizablePane,
12 | Handle as ResizableHandle
13 | };
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/resizable/resizable-pane-group.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/scroll-area/index.ts:
--------------------------------------------------------------------------------
1 | import Scrollbar from './scroll-area-scrollbar.svelte';
2 | import Root from './scroll-area.svelte';
3 |
4 | export {
5 | Root,
6 | Scrollbar,
7 | //,
8 | Root as ScrollArea,
9 | Scrollbar as ScrollAreaScrollbar
10 | };
11 |
--------------------------------------------------------------------------------
/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
26 | {@render children?.()}
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/index.ts:
--------------------------------------------------------------------------------
1 | import { Select as SelectPrimitive } from 'bits-ui';
2 |
3 | import Group from './select-group.svelte';
4 | import Label from './select-label.svelte';
5 | import Item from './select-item.svelte';
6 | import Content from './select-content.svelte';
7 | import Trigger from './select-trigger.svelte';
8 | import Separator from './select-separator.svelte';
9 | import ScrollDownButton from './select-scroll-down-button.svelte';
10 | import ScrollUpButton from './select-scroll-up-button.svelte';
11 |
12 | const Root = SelectPrimitive.Root;
13 |
14 | export {
15 | Root,
16 | Group,
17 | Label,
18 | Item,
19 | Content,
20 | Trigger,
21 | Separator,
22 | ScrollDownButton,
23 | ScrollUpButton,
24 | //
25 | Root as Select,
26 | Group as SelectGroup,
27 | Label as SelectLabel,
28 | Item as SelectItem,
29 | Content as SelectContent,
30 | Trigger as SelectTrigger,
31 | Separator as SelectSeparator,
32 | ScrollDownButton as SelectScrollDownButton,
33 | ScrollUpButton as SelectScrollUpButton
34 | };
35 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-group.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-label.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-scroll-down-button.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-scroll-up-button.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-separator.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
15 |
--------------------------------------------------------------------------------
/src/lib/components/ui/separator/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './separator.svelte';
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Separator
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/separator/separator.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sheet/index.ts:
--------------------------------------------------------------------------------
1 | import { Dialog as SheetPrimitive } from 'bits-ui';
2 | import Trigger from './sheet-trigger.svelte';
3 | import Close from './sheet-close.svelte';
4 | import Overlay from './sheet-overlay.svelte';
5 | import Content from './sheet-content.svelte';
6 | import Header from './sheet-header.svelte';
7 | import Footer from './sheet-footer.svelte';
8 | import Title from './sheet-title.svelte';
9 | import Description from './sheet-description.svelte';
10 |
11 | const Root = SheetPrimitive.Root;
12 | const Portal = SheetPrimitive.Portal;
13 |
14 | export {
15 | Root,
16 | Close,
17 | Trigger,
18 | Portal,
19 | Overlay,
20 | Content,
21 | Header,
22 | Footer,
23 | Title,
24 | Description,
25 | //
26 | Root as Sheet,
27 | Close as SheetClose,
28 | Trigger as SheetTrigger,
29 | Portal as SheetPortal,
30 | Overlay as SheetOverlay,
31 | Content as SheetContent,
32 | Header as SheetHeader,
33 | Footer as SheetFooter,
34 | Title as SheetTitle,
35 | Description as SheetDescription
36 | };
37 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sheet/sheet-close.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sheet/sheet-description.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sheet/sheet-footer.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sheet/sheet-header.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sheet/sheet-overlay.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
19 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sheet/sheet-title.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sheet/sheet-trigger.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/constants.ts:
--------------------------------------------------------------------------------
1 | export const SIDEBAR_COOKIE_NAME = 'sidebar:state';
2 | export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
3 | export const SIDEBAR_WIDTH = '16rem';
4 | export const SIDEBAR_WIDTH_MOBILE = '18rem';
5 | export const SIDEBAR_WIDTH_ICON = '3rem';
6 | export const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
7 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-content.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
23 | {@render children?.()}
24 |
25 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-footer.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 | {@render children?.()}
21 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-group-content.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 | {@render children?.()}
21 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-group-label.svelte:
--------------------------------------------------------------------------------
1 |
27 |
28 | {#if child}
29 | {@render child({ props: mergedProps })}
30 | {:else}
31 |
32 | {@render children?.()}
33 |
34 | {/if}
35 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-group.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 | {@render children?.()}
21 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-header.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 | {@render children?.()}
21 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-input.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-inset.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
23 | {@render children?.()}
24 |
25 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
28 | {@render children?.()}
29 |
30 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-menu-item.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
27 | {#if showIcon}
28 |
29 | {/if}
30 |
35 | {@render children?.()}
36 |
37 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
24 | {@render children?.()}
25 |
26 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-menu.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 | {@render children?.()}
21 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-separator.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sidebar/sidebar-trigger.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
36 |
--------------------------------------------------------------------------------
/src/lib/components/ui/skeleton/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './skeleton.svelte';
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Skeleton
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/skeleton/skeleton.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/slider/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './slider.svelte';
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Slider
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sonner/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Toaster } from './sonner.svelte';
2 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sonner/sonner.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/switch/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './switch.svelte';
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Switch
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/table/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './table.svelte';
2 | import Body from './table-body.svelte';
3 | import Caption from './table-caption.svelte';
4 | import Cell from './table-cell.svelte';
5 | import Footer from './table-footer.svelte';
6 | import Head from './table-head.svelte';
7 | import Header from './table-header.svelte';
8 | import Row from './table-row.svelte';
9 |
10 | export {
11 | Root,
12 | Body,
13 | Caption,
14 | Cell,
15 | Footer,
16 | Head,
17 | Header,
18 | Row,
19 | //
20 | Root as Table,
21 | Body as TableBody,
22 | Caption as TableCaption,
23 | Cell as TableCell,
24 | Footer as TableFooter,
25 | Head as TableHead,
26 | Header as TableHeader,
27 | Row as TableRow
28 | };
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/table/table-body.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/table/table-caption.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/table/table-cell.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 | {@render children?.()}
15 | |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/table/table-footer.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 | tr]:last:border-b-0', className)}
17 | {...restProps}
18 | >
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/table/table-head.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 | {@render children?.()}
18 | |
19 |
--------------------------------------------------------------------------------
/src/lib/components/ui/table/table-header.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | {@render children?.()}
15 |
16 |
--------------------------------------------------------------------------------
/src/lib/components/ui/table/table-row.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/table/table.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 | {@render children?.()}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tabs/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './tabs.svelte';
2 | import Content from './tabs-content.svelte';
3 | import List from './tabs-list.svelte';
4 | import Trigger from './tabs-trigger.svelte';
5 |
6 | export {
7 | Root,
8 | Content,
9 | List,
10 | Trigger,
11 | //
12 | Root as Tabs,
13 | Content as TabsContent,
14 | List as TabsList,
15 | Trigger as TabsTrigger
16 | };
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tabs/tabs-content.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tabs/tabs-list.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tabs/tabs-trigger.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tabs/tabs.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/textarea/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './textarea.svelte';
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Textarea
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/textarea/textarea.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
23 |
--------------------------------------------------------------------------------
/src/lib/components/ui/toggle-group/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './toggle-group.svelte';
2 | import Item from './toggle-group-item.svelte';
3 |
4 | export {
5 | Root,
6 | Item,
7 | //
8 | Root as ToggleGroup,
9 | Item as ToggleGroupItem
10 | };
11 |
--------------------------------------------------------------------------------
/src/lib/components/ui/toggle-group/toggle-group-item.svelte:
--------------------------------------------------------------------------------
1 |
18 |
19 |
35 |
--------------------------------------------------------------------------------
/src/lib/components/ui/toggle/index.ts:
--------------------------------------------------------------------------------
1 | import Root from './toggle.svelte';
2 | export { toggleVariants, type ToggleSize, type ToggleVariant, type ToggleVariants } from './toggle.svelte';
3 |
4 | export {
5 | Root,
6 | //
7 | Root as Toggle
8 | };
9 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tooltip/index.ts:
--------------------------------------------------------------------------------
1 | import { Tooltip as TooltipPrimitive } from 'bits-ui';
2 | import Trigger from './tooltip-trigger.svelte';
3 | import Content from './tooltip-content.svelte';
4 |
5 | const Root = TooltipPrimitive.Root;
6 | const Provider = TooltipPrimitive.Provider;
7 | const Portal = TooltipPrimitive.Portal;
8 |
9 | export {
10 | Root,
11 | Trigger,
12 | Content,
13 | Provider,
14 | Portal,
15 | //
16 | Root as Tooltip,
17 | Content as TooltipContent,
18 | Trigger as TooltipTrigger,
19 | Provider as TooltipProvider,
20 | Portal as TooltipPortal
21 | };
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tooltip/tooltip-trigger.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/db/factories/teamFactory.ts:
--------------------------------------------------------------------------------
1 | import type { Team } from '$lib/db/models/team';
2 | import { faker } from '@faker-js/faker';
3 | import { generateNanoId } from '$lib/utils/helpers/generate';
4 |
5 | export const teamFactory = async (instances: number): Promise => {
6 | return Array.from({ length: instances }, (_, index) => ({
7 | id: index + 1,
8 | publicId: generateNanoId(),
9 | name: faker.company.name(),
10 | avatar: faker.image.avatar(),
11 | createdAt: new Date(),
12 | updatedAt: new Date()
13 | }));
14 | };
15 |
--------------------------------------------------------------------------------
/src/lib/db/factories/userFactory.ts:
--------------------------------------------------------------------------------
1 | import type { User } from '$lib/db/models/user';
2 | import { faker } from '@faker-js/faker';
3 | import { hashPassword } from '$lib/utils/helpers/password';
4 | import { generateNanoId } from '$lib/utils/helpers/generate';
5 |
6 | export const userFactory = async (instances: number): Promise => {
7 | const hashedPassword = await hashPassword('password1234');
8 |
9 | return Array.from({ length: instances }, (_, index) => ({
10 | id: index + 1,
11 | publicId: generateNanoId(),
12 | email: faker.internet.email(),
13 | googleId: null,
14 | firstName: faker.person.firstName(),
15 | lastName: faker.person.lastName(),
16 | hashedPassword,
17 | avatar: faker.image.avatar(),
18 | admin: false,
19 | createdAt: new Date(),
20 | updatedAt: new Date()
21 | }));
22 | };
23 |
--------------------------------------------------------------------------------
/src/lib/db/factories/usersTeamsFactory.ts:
--------------------------------------------------------------------------------
1 | import type { User } from '$lib/db/models/user';
2 | import type { Team } from '$lib/db/models/team';
3 | import { faker } from '@faker-js/faker';
4 |
5 | export const usersTeamsFactory = async (users: User[], teams: Team[]) => {
6 | return Array.from({ length: users.length }, (_, index) => ({
7 | userId: users[index].id,
8 | teamId: teams[index].id,
9 | role: faker.helpers.arrayElement(['admin', 'member'])
10 | }));
11 | };
12 |
--------------------------------------------------------------------------------
/src/lib/db/migrations/meta/_journal.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "7",
3 | "dialect": "sqlite",
4 | "entries": [
5 | {
6 | "idx": 0,
7 | "version": "6",
8 | "when": 1748536656388,
9 | "tag": "0000_lame_jane_foster",
10 | "breakpoints": true
11 | }
12 | ]
13 | }
--------------------------------------------------------------------------------
/src/lib/db/models/session.ts:
--------------------------------------------------------------------------------
1 | import { text, integer, sqliteTable } from 'drizzle-orm/sqlite-core';
2 |
3 | import { User } from './user';
4 |
5 | export const Session = sqliteTable('sessions', {
6 | id: text().notNull().primaryKey(),
7 | userId: integer('user_id')
8 | .notNull()
9 | .references(() => User.id),
10 | expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull()
11 | });
12 |
13 | export type Session = typeof Session.$inferSelect;
14 |
--------------------------------------------------------------------------------
/src/lib/db/models/token.ts:
--------------------------------------------------------------------------------
1 | import { text, integer, sqliteTable } from 'drizzle-orm/sqlite-core';
2 | import { relations } from 'drizzle-orm';
3 | import { generateToken } from '../../utils/helpers/generate';
4 |
5 | import { User } from './user';
6 |
7 | export const Token = sqliteTable('tokens', {
8 | id: integer({ mode: 'number' }).primaryKey({ autoIncrement: true }),
9 | key: text()
10 | .notNull()
11 | .unique()
12 | .$default(() => generateToken()),
13 | userId: integer('user_id')
14 | .references(() => User.id, { onDelete: 'cascade' })
15 | .unique(),
16 | expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull()
17 | });
18 |
19 | export type Token = typeof Token.$inferSelect;
20 |
21 | export const TokenRelations = relations(Token, ({ one }) => ({
22 | user: one(User, {
23 | fields: [Token.userId],
24 | references: [User.id]
25 | })
26 | }));
27 |
--------------------------------------------------------------------------------
/src/lib/db/models/user.ts:
--------------------------------------------------------------------------------
1 | import { text, integer, sqliteTable } from 'drizzle-orm/sqlite-core';
2 | import { sql, relations } from 'drizzle-orm';
3 |
4 | import { UsersTeams } from './team';
5 |
6 | export const User = sqliteTable('users', {
7 | id: integer().notNull().primaryKey({ autoIncrement: true }),
8 | publicId: text('public_id').notNull(),
9 | email: text().unique(),
10 | googleId: integer('google_id').unique(),
11 | firstName: text('first_name').notNull(),
12 | lastName: text('last_name').notNull(),
13 | hashedPassword: text('hashed_password'),
14 | avatar: text(),
15 | admin: integer({ mode: 'boolean' }).notNull().default(false),
16 | createdAt: integer('created_at', { mode: 'timestamp_ms' })
17 | .notNull()
18 | .default(sql`(CURRENT_TIMESTAMP)`),
19 | updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
20 | });
21 |
22 | export const UserRelations = relations(User, ({ many }) => ({
23 | userTeams: many(UsersTeams)
24 | }));
25 |
26 | export type User = typeof User.$inferSelect;
27 |
--------------------------------------------------------------------------------
/src/lib/db/queries/invite.ts:
--------------------------------------------------------------------------------
1 | import { and } from 'drizzle-orm';
2 | // Types
3 | import type { PreparedQueryConfig } from 'drizzle-orm/sqlite-core';
4 | import type { SQLitePreparedQuery } from 'drizzle-orm/sqlite-core';
5 |
6 | // Utils
7 | import { eq, sql } from 'drizzle-orm';
8 |
9 | // Database
10 | import db from '$lib/server/database';
11 | import { Invite } from '$models/invite';
12 |
13 | // SELECT * FROM invites WHERE email = ?
14 | export const getUserPendingInvitesByEmail = db.query.Invite.findMany({
15 | where: and(eq(Invite.email, sql.placeholder('email')), eq(Invite.status, 'pending')),
16 | with: {
17 | team: {
18 | columns: {
19 | id: true,
20 | name: true
21 | }
22 | }
23 | },
24 | orderBy: (invites, { desc }) => [desc(invites.createdAt)]
25 | });
26 |
27 | export const getUserPendingInvitesByEmailQuery: SQLitePreparedQuery =
28 | getUserPendingInvitesByEmail.prepare();
29 | export type GetUserPendingInvitesByEmail = Awaited>;
30 |
--------------------------------------------------------------------------------
/src/lib/db/seed.ts:
--------------------------------------------------------------------------------
1 | import { config } from 'dotenv';
2 | import db from '$lib/server/database';
3 |
4 | import { User } from '$models/user';
5 | import { Team, UsersTeams } from '$models/team';
6 |
7 | import { userFactory } from './factories/userFactory';
8 | import { teamFactory } from './factories/teamFactory';
9 | import { usersTeamsFactory } from './factories/usersTeamsFactory';
10 |
11 | async function seed() {
12 | config();
13 |
14 | if (process.env.NODE_ENV === 'production') {
15 | console.error('Seed script should not be run in production!');
16 | process.exit(1);
17 | }
18 |
19 | console.log(`🌱 Seeding the database with 100 users and 100 teams...`);
20 |
21 | const users = await userFactory(100);
22 | const teams = await teamFactory(100);
23 | const usersTeams = await usersTeamsFactory(users, teams);
24 |
25 | try {
26 | await db.batch([
27 | db.insert(User).values(users),
28 | db.insert(Team).values(teams),
29 | db.insert(UsersTeams).values(usersTeams)
30 | ]);
31 |
32 | console.log('Database seeded.');
33 | } catch (error) {
34 | console.error('Error seeding the database:', error);
35 | process.exit(1);
36 | }
37 | }
38 |
39 | seed();
40 |
--------------------------------------------------------------------------------
/src/lib/hooks/is-mobile.svelte.ts:
--------------------------------------------------------------------------------
1 | import { MediaQuery } from 'svelte/reactivity';
2 |
3 | const MOBILE_BREAKPOINT = 768;
4 |
5 | export class IsMobile extends MediaQuery {
6 | constructor() {
7 | super(`max-width: ${MOBILE_BREAKPOINT - 1}px`);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/lib/server/oauth.ts:
--------------------------------------------------------------------------------
1 | import { PUBLIC_BASE_URL } from '$env/static/public';
2 | import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '$env/static/private';
3 | import { Google } from 'arctic';
4 |
5 | if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) {
6 | throw new Error('GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are required for Google OAuth');
7 | }
8 |
9 | export const google = new Google(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, `${PUBLIC_BASE_URL}/login/google/callback`);
10 |
--------------------------------------------------------------------------------
/src/lib/server/storage.ts:
--------------------------------------------------------------------------------
1 | import { R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_ACCOUNT_ID } from '$env/static/private';
2 | import { S3Client } from '@aws-sdk/client-s3';
3 |
4 | const r2Endpoint = `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`;
5 |
6 | export const s3 = new S3Client({
7 | endpoint: r2Endpoint,
8 | credentials: {
9 | accessKeyId: R2_ACCESS_KEY_ID,
10 | secretAccessKey: R2_SECRET_ACCESS_KEY
11 | },
12 | region: 'auto'
13 | });
14 |
--------------------------------------------------------------------------------
/src/lib/utils/helpers/generate.ts:
--------------------------------------------------------------------------------
1 | import { encodeBase64url } from '@oslojs/encoding';
2 | import { customAlphabet } from 'nanoid';
3 |
4 | const ID_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
5 | const ID_LENGTH = 12;
6 |
7 | export function generateNanoId() {
8 | const nanoid = customAlphabet(ID_ALPHABET, ID_LENGTH);
9 | return nanoid();
10 | }
11 |
12 | export function generateToken() {
13 | const bytes = crypto.getRandomValues(new Uint8Array(64));
14 | const token = encodeBase64url(bytes);
15 | return token;
16 | }
17 |
--------------------------------------------------------------------------------
/src/lib/utils/helpers/password.ts:
--------------------------------------------------------------------------------
1 | import { hash, verify } from '@node-rs/argon2';
2 |
3 | export async function hashPassword(password: string) {
4 | const hashedPassword = await hash(password, {
5 | memoryCost: 19456,
6 | timeCost: 2,
7 | outputLen: 32,
8 | parallelism: 1
9 | });
10 | return hashedPassword;
11 | }
12 |
13 | export async function verifyPassword(hashedPassword: string, password: string) {
14 | const isValid = await verify(hashedPassword, password, {
15 | memoryCost: 19456,
16 | timeCost: 2,
17 | outputLen: 32,
18 | parallelism: 1
19 | });
20 | return isValid;
21 | }
22 |
--------------------------------------------------------------------------------
/src/lib/utils/mail/templates/Welcome.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 | Welcome to SvelteKit Omakase
23 |
24 |
25 | Hey, {userFirstName}!
26 | We're thrilled to have you onboard. ⭐
27 |
28 |
29 |
30 |
31 | Need help? we are just an email away.
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/lib/utils/mail/templates/mailTheme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from 'sailkit';
2 |
3 | export const mailTheme = createTheme({
4 | fonts: [
5 | {
6 | name: 'Outfit',
7 | href: 'https://fonts.googleapis.com/css2?family=Outfit'
8 | }
9 | ],
10 | styles: {
11 | global: {
12 | fontFamily:
13 | 'Outfit, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Helvetica, Arial, sans-serif'
14 | }
15 | }
16 | });
17 |
--------------------------------------------------------------------------------
/src/lib/utils/mail/types.ts:
--------------------------------------------------------------------------------
1 | export interface EmailTemplate {
2 | subject: string;
3 | html: string;
4 | text: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/lib/utils/themes.ts:
--------------------------------------------------------------------------------
1 | export const themes = { default: 'dark', light: 'light', dark: 'dark' };
2 |
--------------------------------------------------------------------------------
/src/lib/utils/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from 'clsx';
2 | import { twMerge } from 'tailwind-merge';
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
8 | export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
9 |
10 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
11 | export type WithoutChild = T extends { child?: any } ? Omit : T;
12 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
13 | export type WithoutChildren = T extends { children?: any } ? Omit : T;
14 | export type WithoutChildrenOrChild = WithoutChildren>;
15 | export type WithElementRef = T & { ref?: U | null };
16 |
--------------------------------------------------------------------------------
/src/routes/(auth)/+layout.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
16 |
17 |
18 |
19 | {@render children?.()}
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/routes/(auth)/login/google/+server.ts:
--------------------------------------------------------------------------------
1 | import type { RequestEvent } from '@sveltejs/kit';
2 | import { generateState, generateCodeVerifier } from 'arctic';
3 | import { google } from '$lib/server/oauth';
4 |
5 | export async function GET(event: RequestEvent): Promise {
6 | const state = generateState();
7 | const codeVerifier = generateCodeVerifier();
8 | const url = google.createAuthorizationURL(state, codeVerifier, ['openid', 'profile', 'email']);
9 |
10 | event.cookies.set('google_oauth_state', state, {
11 | path: '/',
12 | httpOnly: true,
13 | maxAge: 60 * 10, // 10 minutes
14 | sameSite: 'lax'
15 | });
16 | event.cookies.set('google_code_verifier', codeVerifier, {
17 | path: '/',
18 | httpOnly: true,
19 | maxAge: 60 * 10, // 10 minutes
20 | sameSite: 'lax'
21 | });
22 |
23 | return new Response(null, {
24 | status: 302,
25 | headers: {
26 | Location: url.toString()
27 | }
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/src/routes/(auth)/logout/+server.ts:
--------------------------------------------------------------------------------
1 | import type { RequestHandler } from '@sveltejs/kit';
2 | import { redirect } from 'sveltekit-flash-message/server';
3 | import { invalidateSession, deleteSessionTokenCookie } from '$lib/server/auth';
4 | import * as m from '$lib/utils/messages.json';
5 |
6 | export const POST: RequestHandler = async (event) => {
7 | if (!event.locals.session) redirect(302, '/');
8 |
9 | try {
10 | await invalidateSession(event.locals.session.id);
11 | deleteSessionTokenCookie(event);
12 | } catch (error) {
13 | console.log(error);
14 | redirect(
15 | '/',
16 | {
17 | status: 500,
18 | type: 'error',
19 | message: m.general.error
20 | },
21 | event
22 | );
23 | }
24 |
25 | redirect(
26 | '/',
27 | {
28 | type: 'success',
29 | message: m.auth.logout.success
30 | },
31 | event
32 | );
33 | };
34 |
--------------------------------------------------------------------------------
/src/routes/(dashboard)/+layout.server.ts:
--------------------------------------------------------------------------------
1 | import { requireLogin } from '$lib/server/auth';
2 |
3 | export async function load() {
4 | requireLogin();
5 | }
6 |
--------------------------------------------------------------------------------
/src/routes/(dashboard)/dashboard/+page.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | {#each Array(3)}
7 |
10 | {/each}
11 |
14 |
15 |
--------------------------------------------------------------------------------
/src/routes/(dashboard)/settings/+layout.svelte:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
26 |
Settings
27 |
Manage your profile and teams.
28 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |
39 | {@render children?.()}
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/routes/+error.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 | {page.status}: {page.error?.message}
9 |
10 |
11 | Go back home
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/routes/+layout.server.ts:
--------------------------------------------------------------------------------
1 | import { loadFlash } from 'sveltekit-flash-message/server';
2 |
3 | export const load = loadFlash(async (event) => {
4 | return {
5 | user: event.locals.user ?? null
6 | };
7 | });
8 |
--------------------------------------------------------------------------------
/src/routes/+page.server.ts:
--------------------------------------------------------------------------------
1 | import { redirect } from '@sveltejs/kit';
2 |
3 | export async function load({ locals }) {
4 | // redirect to `/dashboard` if logged in
5 | if (locals.user) redirect(302, '/dashboard');
6 | }
7 |
--------------------------------------------------------------------------------
/src/routes/api/upload/+server.ts:
--------------------------------------------------------------------------------
1 | // Env Variables
2 | import { PUBLIC_R2_BUCKET_NAME } from '$env/static/public';
3 |
4 | // Types
5 | import { type RequestHandler } from '@sveltejs/kit';
6 |
7 | // Utils
8 | import { error, json } from '@sveltejs/kit';
9 | import { s3 } from '$lib/server/storage';
10 | import { PutObjectCommand } from '@aws-sdk/client-s3';
11 | import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
12 | import crypto from 'crypto';
13 |
14 | export const POST: RequestHandler = async ({ locals, request }) => {
15 | if (!locals.user) {
16 | error(401, 'Unauthorized');
17 | }
18 |
19 | try {
20 | const { fileType, destinationDirectory } = await request.json();
21 |
22 | const fileName = crypto.randomBytes(16).toString('hex');
23 |
24 | const file = {
25 | Bucket: PUBLIC_R2_BUCKET_NAME,
26 | Key: `${destinationDirectory}/${fileName}`,
27 | ContentType: fileType
28 | };
29 |
30 | const command = new PutObjectCommand(file);
31 | const url = await getSignedUrl(s3, command, { expiresIn: 60000 });
32 |
33 | return json({
34 | presignedUrl: url,
35 | fileName
36 | });
37 | } catch (err) {
38 | console.log(err);
39 | }
40 |
41 | error(500, 'Something went wrong');
42 | };
43 |
--------------------------------------------------------------------------------
/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n00ki/sveltekit-omakase/8de5357d79ffdc52acae042c0731f3b6fda97135/static/favicon.png
--------------------------------------------------------------------------------
/static/fonts/Montserrat-VariableFont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n00ki/sveltekit-omakase/8de5357d79ffdc52acae042c0731f3b6fda97135/static/fonts/Montserrat-VariableFont.ttf
--------------------------------------------------------------------------------
/static/fonts/Sora-VariableFont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n00ki/sveltekit-omakase/8de5357d79ffdc52acae042c0731f3b6fda97135/static/fonts/Sora-VariableFont.ttf
--------------------------------------------------------------------------------
/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /admin
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-auto';
2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | kit: {
7 | adapter: adapter(),
8 |
9 | alias: {
10 | $components: 'src/lib/components',
11 | '$components/*': 'src/lib/components/*',
12 |
13 | $models: 'src/lib/db/models',
14 | '$models/*': 'src/lib/db/models/*',
15 |
16 | $queries: 'src/lib/db/queries',
17 | '$queries/*': 'src/lib/db/queries/*'
18 | }
19 | },
20 |
21 | preprocess: vitePreprocess()
22 | };
23 |
24 | export default config;
25 |
--------------------------------------------------------------------------------
/tests/e2e/homepage.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test';
2 |
3 | test('home page has expected h1', async ({ page }) => {
4 | await page.goto('/');
5 | await expect(page.locator('h1')).toHaveText('SvelteKit Omakase');
6 | });
7 |
8 | test('home page has navigation links', async ({ page }) => {
9 | await page.goto('/');
10 |
11 | // Check for Login button
12 | const loginButton = page.locator('a[href="/login"]');
13 | await expect(loginButton).toBeVisible();
14 | await expect(loginButton).toHaveText('Login');
15 |
16 | // Check for Register button
17 | const registerButton = page.locator('a[href="/register"]');
18 | await expect(registerButton).toBeVisible();
19 | await expect(registerButton).toHaveText('Register');
20 | });
21 |
--------------------------------------------------------------------------------
/tests/unit/demo.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'vitest';
2 |
3 | describe('sum test', () => {
4 | it('adds 1 + 2 to equal 3', () => {
5 | expect(1 + 2).toBe(3);
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import { sveltekit } from '@sveltejs/kit/vite';
3 | import { svelteTesting } from '@testing-library/svelte/vite';
4 | import tailwindcss from '@tailwindcss/vite';
5 |
6 | export default defineConfig({
7 | plugins: [sveltekit(), tailwindcss()],
8 | test: {
9 | workspace: [
10 | {
11 | extends: './vite.config.ts',
12 | plugins: [svelteTesting()],
13 | test: {
14 | name: 'client',
15 | environment: 'jsdom',
16 | clearMocks: true,
17 | include: ['tests/**/*.svelte.{test,spec}.{js,ts}'],
18 | exclude: ['src/lib/server/**'],
19 | setupFiles: ['./vitest-setup-client.ts']
20 | }
21 | },
22 | {
23 | extends: './vite.config.ts',
24 | test: {
25 | name: 'server',
26 | environment: 'node',
27 | include: ['tests/unit/**/*.{test,spec}.{js,ts}'],
28 | exclude: ['src/**/*.svelte.{test,spec}.{js,ts}']
29 | }
30 | }
31 | ]
32 | }
33 | });
34 |
--------------------------------------------------------------------------------
/vitest-setup-client.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom/vitest';
2 | import { vi } from 'vitest';
3 |
4 | // required for svelte5 + jsdom as jsdom does not support matchMedia
5 | Object.defineProperty(window, 'matchMedia', {
6 | writable: true,
7 | enumerable: true,
8 | value: vi.fn().mockImplementation((query) => ({
9 | matches: false,
10 | media: query,
11 | onchange: null,
12 | addEventListener: vi.fn(),
13 | removeEventListener: vi.fn(),
14 | dispatchEvent: vi.fn()
15 | }))
16 | });
17 |
18 | // more mocks here if needed
19 |
--------------------------------------------------------------------------------