diff --git a/apps/desktop/.gitignore b/apps/desktop/.gitignore new file mode 100644 index 0000000..3aee7df --- /dev/null +++ b/apps/desktop/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +.router diff --git a/apps/desktop/.prettierignore b/apps/desktop/.prettierignore new file mode 100644 index 0000000..7d74fe2 --- /dev/null +++ b/apps/desktop/.prettierignore @@ -0,0 +1,9 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock +bun.lock +bun.lockb + +# Miscellaneous +/static/ diff --git a/apps/desktop/.prettierrc b/apps/desktop/.prettierrc new file mode 100644 index 0000000..f305c48 --- /dev/null +++ b/apps/desktop/.prettierrc @@ -0,0 +1,22 @@ +{ + "useTabs": true, + "singleQuote": true, + "semi": false, + "trailingComma": "all", + "printWidth": 70, + "bracketSameLine": true, + "htmlWhitespaceSensitivity": "ignore", + "plugins": [ + "prettier-plugin-tailwindcss", + "prettier-plugin-svelte" + ], + "tailwindStylesheet": "./src/app.css", + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} diff --git a/apps/desktop/README.md b/apps/desktop/README.md new file mode 100644 index 0000000..e004545 --- /dev/null +++ b/apps/desktop/README.md @@ -0,0 +1,9 @@ +# sv-router | File-Based Template + +Check the [documentation](https://sv-router.vercel.app/guide/file-based/route-definition). + +Start the dev server: + +``` +npm run dev +``` diff --git a/apps/desktop/components.json b/apps/desktop/components.json new file mode 100644 index 0000000..afd7794 --- /dev/null +++ b/apps/desktop/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://shadcn-svelte.com/schema.json", + "tailwind": { + "css": "src/app.css", + "baseColor": "neutral" + }, + "aliases": { + "components": "$lib/components", + "utils": "$lib/utils", + "ui": "$lib/components/ui", + "hooks": "$lib/hooks", + "lib": "$lib" + }, + "typescript": true, + "registry": "https://shadcn-svelte.com/registry", + "style": "lyra", + "iconLibrary": "remixicon", + "menuColor": "default", + "menuAccent": "bold" +} diff --git a/apps/desktop/electrobun.config.ts b/apps/desktop/electrobun.config.ts new file mode 100644 index 0000000..6076d67 --- /dev/null +++ b/apps/desktop/electrobun.config.ts @@ -0,0 +1,27 @@ +import type { ElectrobunConfig } from 'electrobun' + +export default { + app: { + name: 'koins', + identifier: 'koins.tzgyn.com', + version: '0.0.1', + }, + build: { + // Vite builds to dist/, we copy from there + copy: { + 'dist/index.html': 'views/mainview/index.html', + 'dist/assets': 'views/mainview/assets', + }, + // Ignore Vite output in watch mode — HMR handles view rebuilds separately + watchIgnore: ['dist/**'], + mac: { + bundleCEF: false, + }, + linux: { + bundleCEF: false, + }, + win: { + bundleCEF: false, + }, + }, +} satisfies ElectrobunConfig diff --git a/apps/desktop/icon.iconset/Contents.json b/apps/desktop/icon.iconset/Contents.json new file mode 100644 index 0000000..927f576 --- /dev/null +++ b/apps/desktop/icon.iconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images": [ + { + "idiom": "mac", + "filename": "icon_16x16.png", + "scale": "1x", + "size": "16x16" + }, + { + "idiom": "mac", + "filename": "icon_16x16@2x.png", + "scale": "2x", + "size": "16x16" + }, + { + "idiom": "mac", + "filename": "icon_32x32.png", + "scale": "1x", + "size": "32x32" + }, + { + "idiom": "mac", + "filename": "icon_32x32@2x.png", + "scale": "2x", + "size": "32x32" + }, + { + "idiom": "mac", + "filename": "icon_128x128.png", + "scale": "1x", + "size": "128x128" + }, + { + "idiom": "mac", + "filename": "icon_128x128@2x.png", + "scale": "2x", + "size": "128x128" + }, + { + "idiom": "mac", + "filename": "icon_256x256.png", + "scale": "1x", + "size": "256x256" + }, + { + "idiom": "mac", + "filename": "icon_256x256@2x.png", + "scale": "2x", + "size": "256x256" + }, + { + "idiom": "mac", + "filename": "icon_512x512.png", + "scale": "1x", + "size": "512x512" + }, + { + "idiom": "mac", + "filename": "icon_512x512@2x.png", + "scale": "2x", + "size": "512x512" + } + ], + "info": { + "author": "icon-generator", + "version": 1 + } +} \ No newline at end of file diff --git a/apps/desktop/icon.iconset/generator_link.txt b/apps/desktop/icon.iconset/generator_link.txt new file mode 100644 index 0000000..5315add --- /dev/null +++ b/apps/desktop/icon.iconset/generator_link.txt @@ -0,0 +1 @@ +https://icon.msgbyte.com/ \ No newline at end of file diff --git a/apps/desktop/icon.iconset/icon_128x128.png b/apps/desktop/icon.iconset/icon_128x128.png new file mode 100644 index 0000000..d7d00eb Binary files /dev/null and b/apps/desktop/icon.iconset/icon_128x128.png differ diff --git a/apps/desktop/icon.iconset/icon_128x128@2x.png b/apps/desktop/icon.iconset/icon_128x128@2x.png new file mode 100644 index 0000000..15229bd Binary files /dev/null and b/apps/desktop/icon.iconset/icon_128x128@2x.png differ diff --git a/apps/desktop/icon.iconset/icon_16x16.png b/apps/desktop/icon.iconset/icon_16x16.png new file mode 100644 index 0000000..7a6b1ec Binary files /dev/null and b/apps/desktop/icon.iconset/icon_16x16.png differ diff --git a/apps/desktop/icon.iconset/icon_16x16@2x.png b/apps/desktop/icon.iconset/icon_16x16@2x.png new file mode 100644 index 0000000..2d9a796 Binary files /dev/null and b/apps/desktop/icon.iconset/icon_16x16@2x.png differ diff --git a/apps/desktop/icon.iconset/icon_256x256.png b/apps/desktop/icon.iconset/icon_256x256.png new file mode 100644 index 0000000..15229bd Binary files /dev/null and b/apps/desktop/icon.iconset/icon_256x256.png differ diff --git a/apps/desktop/icon.iconset/icon_256x256@2x.png b/apps/desktop/icon.iconset/icon_256x256@2x.png new file mode 100644 index 0000000..d3e86c3 Binary files /dev/null and b/apps/desktop/icon.iconset/icon_256x256@2x.png differ diff --git a/apps/desktop/icon.iconset/icon_32x32.png b/apps/desktop/icon.iconset/icon_32x32.png new file mode 100644 index 0000000..2d9a796 Binary files /dev/null and b/apps/desktop/icon.iconset/icon_32x32.png differ diff --git a/apps/desktop/icon.iconset/icon_32x32@2x.png b/apps/desktop/icon.iconset/icon_32x32@2x.png new file mode 100644 index 0000000..af8ea27 Binary files /dev/null and b/apps/desktop/icon.iconset/icon_32x32@2x.png differ diff --git a/apps/desktop/icon.iconset/icon_512x512.png b/apps/desktop/icon.iconset/icon_512x512.png new file mode 100644 index 0000000..d3e86c3 Binary files /dev/null and b/apps/desktop/icon.iconset/icon_512x512.png differ diff --git a/apps/desktop/icon.iconset/icon_512x512@2x.png b/apps/desktop/icon.iconset/icon_512x512@2x.png new file mode 100644 index 0000000..ca0bf1f Binary files /dev/null and b/apps/desktop/icon.iconset/icon_512x512@2x.png differ diff --git a/apps/desktop/index.html b/apps/desktop/index.html new file mode 100644 index 0000000..466f110 --- /dev/null +++ b/apps/desktop/index.html @@ -0,0 +1,13 @@ + + + + + + + sv-router Starter + + +
+ + + diff --git a/apps/desktop/package.json b/apps/desktop/package.json new file mode 100644 index 0000000..cc9cacf --- /dev/null +++ b/apps/desktop/package.json @@ -0,0 +1,51 @@ +{ + "name": "koins", + "private": true, + "type": "module", + "scripts": { + "start": "vite build && electrobun dev", + "dev": "electrobun dev --watch", + "dev:hmr": "concurrently \"bun run hmr\" \"bun run start\"", + "hmr": "vite --port 5173", + "build:dev": "vite build && electrobun build", + "build:canary": "vite build && electrobun build --env=canary", + "build:stable": "vite build && electrobun build --env=stable" + }, + "dependencies": { + "electrobun": "^1.18.1", + "ethers": "^6.16.0", + "mode-watcher": "^1.1.0", + "sv-router": "latest" + }, + "devDependencies": { + "@fontsource-variable/geist-mono": "^5.2.7", + "@internationalized/date": "^3.12.0", + "@lucide/svelte": "^1.7.0", + "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@tailwindcss/vite": "^4.2.2", + "@tanstack/table-core": "^8.21.3", + "bits-ui": "^2.16.5", + "clsx": "^2.1.1", + "concurrently": "^9.2.1", + "embla-carousel-svelte": "^8.6.0", + "formsnap": "^2.0.1", + "layerchart": "2.0.0-next.48", + "paneforge": "^1.0.2", + "prettier": "^3.8.1", + "prettier-plugin-svelte": "^3.5.1", + "prettier-plugin-tailwindcss": "^0.7.2", + "remixicon-svelte": "^0.0.5", + "shadcn-svelte": "^1.2.7", + "svelte": "^5.55.3", + "svelte-check": "^4.4.6", + "svelte-sonner": "^1.1.0", + "sveltekit-superforms": "^2.30.0", + "tailwind-merge": "^3.5.0", + "tailwind-variants": "^3.2.2", + "tailwindcss": "^4.2.2", + "tw-animate-css": "^1.4.0", + "typescript": "^6.0.2", + "vaul-svelte": "^1.0.0-next.7", + "vite": "^8.0.8" + } +} diff --git a/apps/desktop/src/App.svelte b/apps/desktop/src/App.svelte new file mode 100644 index 0000000..f0087fe --- /dev/null +++ b/apps/desktop/src/App.svelte @@ -0,0 +1,13 @@ + + + + Koins + + + + diff --git a/apps/desktop/src/app.css b/apps/desktop/src/app.css new file mode 100644 index 0000000..e19f266 --- /dev/null +++ b/apps/desktop/src/app.css @@ -0,0 +1,130 @@ +@import "tailwindcss"; +@import "tw-animate-css"; +@import "shadcn-svelte/tailwind.css"; +@import "@fontsource-variable/geist-mono"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.553 0.195 38.402); + --primary-foreground: oklch(0.98 0.016 73.684); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.553 0.195 38.402); + --accent-foreground: oklch(0.98 0.016 73.684); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.837 0.128 66.29); + --chart-2: oklch(0.705 0.213 47.604); + --chart-3: oklch(0.646 0.222 41.116); + --chart-4: oklch(0.553 0.195 38.402); + --chart-5: oklch(0.47 0.157 37.304); + --radius: 0rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.646 0.222 41.116); + --sidebar-primary-foreground: oklch(0.98 0.016 73.684); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.47 0.157 37.304); + --primary-foreground: oklch(0.98 0.016 73.684); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.47 0.157 37.304); + --accent-foreground: oklch(0.98 0.016 73.684); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.837 0.128 66.29); + --chart-2: oklch(0.705 0.213 47.604); + --chart-3: oklch(0.646 0.222 41.116); + --chart-4: oklch(0.553 0.195 38.402); + --chart-5: oklch(0.47 0.157 37.304); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.705 0.213 47.604); + --sidebar-primary-foreground: oklch(0.98 0.016 73.684); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@theme inline { + --font-mono: 'Geist Mono Variable', monospace; + --font-heading: 'Geist Mono Variable', monospace; + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --color-foreground: var(--foreground); + --color-background: var(--background); + --radius-sm: calc(var(--radius) * 0.6); + --radius-md: calc(var(--radius) * 0.8); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) * 1.4); + --radius-2xl: calc(var(--radius) * 1.8); + --radius-3xl: calc(var(--radius) * 2.2); + --radius-4xl: calc(var(--radius) * 2.6); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } + html { + @apply font-mono; + } +} diff --git a/apps/desktop/src/bun/index.ts b/apps/desktop/src/bun/index.ts new file mode 100644 index 0000000..64f241c --- /dev/null +++ b/apps/desktop/src/bun/index.ts @@ -0,0 +1,111 @@ +import { + ApplicationMenu, + BrowserView, + BrowserWindow, + Updater, + defineElectrobunRPC, + type RPCSchema, +} from 'electrobun/bun' + +const DEV_SERVER_PORT = 5173 +const DEV_SERVER_URL = `http://localhost:${DEV_SERVER_PORT}` + +async function getMainViewUrl(): Promise { + const channel = await Updater.localInfo.channel() + if (channel === 'dev') { + try { + await fetch(DEV_SERVER_URL, { method: 'HEAD' }) + console.log( + `HMR enabled: Using Vite dev server at ${DEV_SERVER_URL}`, + ) + return DEV_SERVER_URL + } catch { + console.log( + "Vite dev server not running. Run 'bun run dev:hmr' for HMR support.", + ) + } + } + return 'views://mainview/index.html' +} + +ApplicationMenu.setApplicationMenu([ + { + label: 'File', + submenu: [{ role: 'quit' }], + }, + { + label: 'Edit', + submenu: [ + { role: 'undo' }, + { role: 'redo' }, + { type: 'divider' }, + { role: 'cut' }, + { role: 'copy' }, + { role: 'paste' }, + { role: 'delete' }, + { type: 'divider' }, + { role: 'selectAll' }, + ], + }, + { + label: 'Window', + submenu: [{ role: 'minimize' }, { role: 'zoom' }], + }, +]) + +const url = await getMainViewUrl() + +type RPC = { + bun: RPCSchema<{ + requests: { + getSecret: { + params: { + service: string + name: string + } + response: Promise + } + setSecret: { + params: { + service: string + name: string + value: string + } + response: Promise + } + } + messages: {} + }> + webview: RPCSchema<{ + requests: {} + messages: {} + }> +} +const rpc = BrowserView.defineRPC({ + maxRequestTime: 10000, + handlers: { + requests: { + getSecret: async ({ name, service }) => { + return await Bun.secrets.get({ name, service }) + }, + setSecret: async ({ name, service, value }) => { + await Bun.secrets.set({ name, service, value }) + }, + }, + messages: {}, + }, +}) + +const win = new BrowserWindow({ + title: 'Koins', + url, + rpc, + frame: { + width: 900, + height: 700, + x: 200, + y: 200, + }, +}) + +console.log('Svelte app started!') diff --git a/apps/desktop/src/lib/components/button.svelte b/apps/desktop/src/lib/components/button.svelte new file mode 100644 index 0000000..d0d3b32 --- /dev/null +++ b/apps/desktop/src/lib/components/button.svelte @@ -0,0 +1,80 @@ + + + + + diff --git a/apps/desktop/src/lib/components/ui/accordion/accordion-content.svelte b/apps/desktop/src/lib/components/ui/accordion/accordion-content.svelte new file mode 100644 index 0000000..24f1f41 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/accordion/accordion-content.svelte @@ -0,0 +1,27 @@ + + + +
+ {@render children?.()} +
+
diff --git a/apps/desktop/src/lib/components/ui/accordion/accordion-item.svelte b/apps/desktop/src/lib/components/ui/accordion/accordion-item.svelte new file mode 100644 index 0000000..9d3919d --- /dev/null +++ b/apps/desktop/src/lib/components/ui/accordion/accordion-item.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/accordion/accordion-trigger.svelte b/apps/desktop/src/lib/components/ui/accordion/accordion-trigger.svelte new file mode 100644 index 0000000..8a35dc5 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/accordion/accordion-trigger.svelte @@ -0,0 +1,32 @@ + + + + + {@render children?.()} + + + diff --git a/apps/desktop/src/lib/components/ui/accordion/accordion.svelte b/apps/desktop/src/lib/components/ui/accordion/accordion.svelte new file mode 100644 index 0000000..b19a3c5 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/accordion/accordion.svelte @@ -0,0 +1,19 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/accordion/index.ts b/apps/desktop/src/lib/components/ui/accordion/index.ts new file mode 100644 index 0000000..ac343a1 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/accordion/index.ts @@ -0,0 +1,16 @@ +import Root from "./accordion.svelte"; +import Content from "./accordion-content.svelte"; +import Item from "./accordion-item.svelte"; +import Trigger from "./accordion-trigger.svelte"; + +export { + Root, + Content, + Item, + Trigger, + // + Root as Accordion, + Content as AccordionContent, + Item as AccordionItem, + Trigger as AccordionTrigger, +}; diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte new file mode 100644 index 0000000..7e63004 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte @@ -0,0 +1,27 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte new file mode 100644 index 0000000..a4ce03c --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte @@ -0,0 +1,27 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte new file mode 100644 index 0000000..710727d --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte @@ -0,0 +1,32 @@ + + + + + + diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte new file mode 100644 index 0000000..7b26f8a --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte new file mode 100644 index 0000000..4d133b7 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte new file mode 100644 index 0000000..d6df4d3 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte new file mode 100644 index 0000000..4db6d7e --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte new file mode 100644 index 0000000..9ffcc85 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte new file mode 100644 index 0000000..f0a19a8 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte new file mode 100644 index 0000000..8e55d89 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte new file mode 100644 index 0000000..b22d1d5 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog.svelte b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog.svelte new file mode 100644 index 0000000..7ea78bb --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/alert-dialog.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/alert-dialog/index.ts b/apps/desktop/src/lib/components/ui/alert-dialog/index.ts new file mode 100644 index 0000000..ca81c2a --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert-dialog/index.ts @@ -0,0 +1,40 @@ +import Root from "./alert-dialog.svelte"; +import Portal from "./alert-dialog-portal.svelte"; +import Trigger from "./alert-dialog-trigger.svelte"; +import Title from "./alert-dialog-title.svelte"; +import Action from "./alert-dialog-action.svelte"; +import Cancel from "./alert-dialog-cancel.svelte"; +import Footer from "./alert-dialog-footer.svelte"; +import Header from "./alert-dialog-header.svelte"; +import Overlay from "./alert-dialog-overlay.svelte"; +import Content from "./alert-dialog-content.svelte"; +import Description from "./alert-dialog-description.svelte"; +import Media from "./alert-dialog-media.svelte"; + +export { + Root, + Title, + Action, + Cancel, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Media, + // + Root as AlertDialog, + Title as AlertDialogTitle, + Action as AlertDialogAction, + Cancel as AlertDialogCancel, + Portal as AlertDialogPortal, + Footer as AlertDialogFooter, + Header as AlertDialogHeader, + Trigger as AlertDialogTrigger, + Overlay as AlertDialogOverlay, + Content as AlertDialogContent, + Description as AlertDialogDescription, + Media as AlertDialogMedia, +}; diff --git a/apps/desktop/src/lib/components/ui/alert/alert-action.svelte b/apps/desktop/src/lib/components/ui/alert/alert-action.svelte new file mode 100644 index 0000000..0f90609 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert/alert-action.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/apps/desktop/src/lib/components/ui/alert/alert-description.svelte b/apps/desktop/src/lib/components/ui/alert/alert-description.svelte new file mode 100644 index 0000000..e62a009 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert/alert-description.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/apps/desktop/src/lib/components/ui/alert/alert-title.svelte b/apps/desktop/src/lib/components/ui/alert/alert-title.svelte new file mode 100644 index 0000000..7591a87 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert/alert-title.svelte @@ -0,0 +1,23 @@ + + +
svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3", + className + )} + {...restProps} +> + {@render children?.()} +
diff --git a/apps/desktop/src/lib/components/ui/alert/alert.svelte b/apps/desktop/src/lib/components/ui/alert/alert.svelte new file mode 100644 index 0000000..b2eecfa --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert/alert.svelte @@ -0,0 +1,43 @@ + + + + + diff --git a/apps/desktop/src/lib/components/ui/alert/index.ts b/apps/desktop/src/lib/components/ui/alert/index.ts new file mode 100644 index 0000000..071b113 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/alert/index.ts @@ -0,0 +1,17 @@ +import Root from "./alert.svelte"; +import Description from "./alert-description.svelte"; +import Title from "./alert-title.svelte"; +import Action from "./alert-action.svelte"; +export { alertVariants, type AlertVariant } from "./alert.svelte"; + +export { + Root, + Description, + Title, + Action, + // + Root as Alert, + Description as AlertDescription, + Title as AlertTitle, + Action as AlertAction, +}; diff --git a/apps/desktop/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte b/apps/desktop/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte new file mode 100644 index 0000000..815aab0 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/aspect-ratio/index.ts b/apps/desktop/src/lib/components/ui/aspect-ratio/index.ts new file mode 100644 index 0000000..985c75f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/aspect-ratio/index.ts @@ -0,0 +1,3 @@ +import Root from "./aspect-ratio.svelte"; + +export { Root, Root as AspectRatio }; diff --git a/apps/desktop/src/lib/components/ui/avatar/avatar-badge.svelte b/apps/desktop/src/lib/components/ui/avatar/avatar-badge.svelte new file mode 100644 index 0000000..ca9d9ed --- /dev/null +++ b/apps/desktop/src/lib/components/ui/avatar/avatar-badge.svelte @@ -0,0 +1,26 @@ + + +svg]:hidden", + "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", + "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", + className + )} + {...restProps} +> + {@render children?.()} + diff --git a/apps/desktop/src/lib/components/ui/avatar/avatar-fallback.svelte b/apps/desktop/src/lib/components/ui/avatar/avatar-fallback.svelte new file mode 100644 index 0000000..63c8b02 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/avatar/avatar-fallback.svelte @@ -0,0 +1,20 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/avatar/avatar-group-count.svelte b/apps/desktop/src/lib/components/ui/avatar/avatar-group-count.svelte new file mode 100644 index 0000000..536c308 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/avatar/avatar-group-count.svelte @@ -0,0 +1,23 @@ + + +
svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 ring-background relative flex shrink-0 items-center justify-center ring-2", + className + )} + {...restProps} +> + {@render children?.()} +
diff --git a/apps/desktop/src/lib/components/ui/avatar/avatar-group.svelte b/apps/desktop/src/lib/components/ui/avatar/avatar-group.svelte new file mode 100644 index 0000000..78714fc --- /dev/null +++ b/apps/desktop/src/lib/components/ui/avatar/avatar-group.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/apps/desktop/src/lib/components/ui/avatar/avatar-image.svelte b/apps/desktop/src/lib/components/ui/avatar/avatar-image.svelte new file mode 100644 index 0000000..f41cf8f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/avatar/avatar-image.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/avatar/avatar.svelte b/apps/desktop/src/lib/components/ui/avatar/avatar.svelte new file mode 100644 index 0000000..ea6fde5 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/avatar/avatar.svelte @@ -0,0 +1,26 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/avatar/index.ts b/apps/desktop/src/lib/components/ui/avatar/index.ts new file mode 100644 index 0000000..38ccef8 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/avatar/index.ts @@ -0,0 +1,22 @@ +import Root from "./avatar.svelte"; +import Image from "./avatar-image.svelte"; +import Fallback from "./avatar-fallback.svelte"; +import Badge from "./avatar-badge.svelte"; +import Group from "./avatar-group.svelte"; +import GroupCount from "./avatar-group-count.svelte"; + +export { + Root, + Image, + Fallback, + Badge, + Group, + GroupCount, + // + Root as Avatar, + Image as AvatarImage, + Fallback as AvatarFallback, + Badge as AvatarBadge, + Group as AvatarGroup, + GroupCount as AvatarGroupCount, +}; diff --git a/apps/desktop/src/lib/components/ui/badge/badge.svelte b/apps/desktop/src/lib/components/ui/badge/badge.svelte new file mode 100644 index 0000000..2cfe06f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/badge/badge.svelte @@ -0,0 +1,49 @@ + + + + + + {@render children?.()} + diff --git a/apps/desktop/src/lib/components/ui/badge/index.ts b/apps/desktop/src/lib/components/ui/badge/index.ts new file mode 100644 index 0000000..64e0aa9 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/badge/index.ts @@ -0,0 +1,2 @@ +export { default as Badge } from "./badge.svelte"; +export { badgeVariants, type BadgeVariant } from "./badge.svelte"; diff --git a/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte new file mode 100644 index 0000000..17e314a --- /dev/null +++ b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte @@ -0,0 +1,23 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte new file mode 100644 index 0000000..e9c77ea --- /dev/null +++ b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte @@ -0,0 +1,20 @@ + + +
  • + {@render children?.()} +
  • diff --git a/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte new file mode 100644 index 0000000..e6bc17d --- /dev/null +++ b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte @@ -0,0 +1,31 @@ + + +{#if child} + {@render child({ props: attrs })} +{:else} + + {@render children?.()} + +{/if} diff --git a/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte new file mode 100644 index 0000000..44fd50b --- /dev/null +++ b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte @@ -0,0 +1,20 @@ + + +
      + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte new file mode 100644 index 0000000..5fb6979 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte new file mode 100644 index 0000000..f6307ec --- /dev/null +++ b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte @@ -0,0 +1,27 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb.svelte b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb.svelte new file mode 100644 index 0000000..29ea3f5 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/breadcrumb/breadcrumb.svelte @@ -0,0 +1,22 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/breadcrumb/index.ts b/apps/desktop/src/lib/components/ui/breadcrumb/index.ts new file mode 100644 index 0000000..dc914ec --- /dev/null +++ b/apps/desktop/src/lib/components/ui/breadcrumb/index.ts @@ -0,0 +1,25 @@ +import Root from "./breadcrumb.svelte"; +import Ellipsis from "./breadcrumb-ellipsis.svelte"; +import Item from "./breadcrumb-item.svelte"; +import Separator from "./breadcrumb-separator.svelte"; +import Link from "./breadcrumb-link.svelte"; +import List from "./breadcrumb-list.svelte"; +import Page from "./breadcrumb-page.svelte"; + +export { + Root, + Ellipsis, + Item, + Separator, + Link, + List, + Page, + // + Root as Breadcrumb, + Ellipsis as BreadcrumbEllipsis, + Item as BreadcrumbItem, + Separator as BreadcrumbSeparator, + Link as BreadcrumbLink, + List as BreadcrumbList, + Page as BreadcrumbPage, +}; diff --git a/apps/desktop/src/lib/components/ui/button-group/button-group-separator.svelte b/apps/desktop/src/lib/components/ui/button-group/button-group-separator.svelte new file mode 100644 index 0000000..a13f023 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/button-group/button-group-separator.svelte @@ -0,0 +1,23 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/button-group/button-group-text.svelte b/apps/desktop/src/lib/components/ui/button-group/button-group-text.svelte new file mode 100644 index 0000000..d5b607f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/button-group/button-group-text.svelte @@ -0,0 +1,28 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} +
    + {@render mergedProps.children?.()} +
    +{/if} diff --git a/apps/desktop/src/lib/components/ui/button-group/button-group.svelte b/apps/desktop/src/lib/components/ui/button-group/button-group.svelte new file mode 100644 index 0000000..6137433 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/button-group/button-group.svelte @@ -0,0 +1,46 @@ + + + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/button-group/index.ts b/apps/desktop/src/lib/components/ui/button-group/index.ts new file mode 100644 index 0000000..664167f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/button-group/index.ts @@ -0,0 +1,15 @@ +import Root, { buttonGroupVariants, type ButtonGroupOrientation } from "./button-group.svelte"; +import Text from "./button-group-text.svelte"; +import Separator from "./button-group-separator.svelte"; + +export { + Root, + Text, + Separator, + buttonGroupVariants, + type ButtonGroupOrientation, + // + Root as ButtonGroup, + Text as ButtonGroupText, + Separator as ButtonGroupSeparator, +}; diff --git a/apps/desktop/src/lib/components/ui/button/button.svelte b/apps/desktop/src/lib/components/ui/button/button.svelte new file mode 100644 index 0000000..9d3b378 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/button/button.svelte @@ -0,0 +1,89 @@ + + + + +{#if href} + + {@render children?.()} + +{:else} + +{/if} diff --git a/apps/desktop/src/lib/components/ui/button/index.ts b/apps/desktop/src/lib/components/ui/button/index.ts new file mode 100644 index 0000000..5414d9d --- /dev/null +++ b/apps/desktop/src/lib/components/ui/button/index.ts @@ -0,0 +1,17 @@ +import Root, { + type ButtonProps, + type ButtonSize, + type ButtonVariant, + buttonVariants +} from './button.svelte'; + +export { + Root, + type ButtonProps as Props, + // + Root as Button, + buttonVariants, + type ButtonProps, + type ButtonSize, + type ButtonVariant +}; diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-caption.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-caption.svelte new file mode 100644 index 0000000..5c93037 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-caption.svelte @@ -0,0 +1,76 @@ + + +{#snippet MonthSelect()} + { + if (!placeholder) return; + const v = Number.parseInt(e.currentTarget.value); + const newPlaceholder = placeholder.set({ month: v }); + placeholder = newPlaceholder.subtract({ months: monthIndex }); + }} + /> +{/snippet} + +{#snippet YearSelect()} + +{/snippet} + +{#if captionLayout === "dropdown"} + {@render MonthSelect()} + {@render YearSelect()} +{:else if captionLayout === "dropdown-months"} + {@render MonthSelect()} + {#if placeholder} + {formatYear(placeholder)} + {/if} +{:else if captionLayout === "dropdown-years"} + {#if placeholder} + {formatMonth(placeholder)} + {/if} + {@render YearSelect()} +{:else} + {formatMonth(month)} {formatYear(month)} +{/if} diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-cell.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-cell.svelte new file mode 100644 index 0000000..7ceb5e1 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-cell.svelte @@ -0,0 +1,19 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-day.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-day.svelte new file mode 100644 index 0000000..edc8c9d --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-day.svelte @@ -0,0 +1,33 @@ + + +span]:text-xs [&>span]:opacity-70", + className + )} + {...restProps} +/> diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-grid-body.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-grid-body.svelte new file mode 100644 index 0000000..8cd86de --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-grid-body.svelte @@ -0,0 +1,12 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-grid-head.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-grid-head.svelte new file mode 100644 index 0000000..333edc4 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-grid-head.svelte @@ -0,0 +1,12 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-grid-row.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-grid-row.svelte new file mode 100644 index 0000000..9032236 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-grid-row.svelte @@ -0,0 +1,12 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-grid.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-grid.svelte new file mode 100644 index 0000000..5fbb18f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-grid.svelte @@ -0,0 +1,16 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-head-cell.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-head-cell.svelte new file mode 100644 index 0000000..131807e --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-head-cell.svelte @@ -0,0 +1,19 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-header.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-header.svelte new file mode 100644 index 0000000..c39e955 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-header.svelte @@ -0,0 +1,19 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-heading.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-heading.svelte new file mode 100644 index 0000000..a9b9810 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-heading.svelte @@ -0,0 +1,16 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-month-select.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-month-select.svelte new file mode 100644 index 0000000..a8f2abb --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-month-select.svelte @@ -0,0 +1,48 @@ + + + + + {#snippet child({ props, monthItems, selectedMonthItem })} + + + {/snippet} + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-month.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-month.svelte new file mode 100644 index 0000000..7bdafe7 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-month.svelte @@ -0,0 +1,15 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-months.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-months.svelte new file mode 100644 index 0000000..f717a9d --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-months.svelte @@ -0,0 +1,19 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-nav.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-nav.svelte new file mode 100644 index 0000000..27f33d7 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-nav.svelte @@ -0,0 +1,19 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-next-button.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-next-button.svelte new file mode 100644 index 0000000..0368d85 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-next-button.svelte @@ -0,0 +1,36 @@ + + +{#snippet Fallback()} + +{/snippet} + + + {#if children} + {@render children?.()} + {:else} + {@render Fallback()} + {/if} + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-prev-button.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-prev-button.svelte new file mode 100644 index 0000000..1e08d77 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-prev-button.svelte @@ -0,0 +1,36 @@ + + +{#snippet Fallback()} + +{/snippet} + + + {#if children} + {@render children?.()} + {:else} + {@render Fallback()} + {/if} + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar-year-select.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar-year-select.svelte new file mode 100644 index 0000000..bd65651 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar-year-select.svelte @@ -0,0 +1,47 @@ + + + + + {#snippet child({ props, yearItems, selectedYearItem })} + + + {/snippet} + + diff --git a/apps/desktop/src/lib/components/ui/calendar/calendar.svelte b/apps/desktop/src/lib/components/ui/calendar/calendar.svelte new file mode 100644 index 0000000..b80f67b --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/calendar.svelte @@ -0,0 +1,115 @@ + + + + + {#snippet children({ months, weekdays })} + + + + + + {#each months as month, monthIndex (month)} + + + + + + + + {#each weekdays as weekday, i (i)} + + {weekday.slice(0, 2)} + + {/each} + + + + {#each month.weeks as weekDates (weekDates)} + + {#each weekDates as date (date)} + + {#if day} + {@render day({ + day: date, + outsideMonth: !isEqualMonth(date, month.value), + })} + {:else} + + {/if} + + {/each} + + {/each} + + + + {/each} + + {/snippet} + diff --git a/apps/desktop/src/lib/components/ui/calendar/index.ts b/apps/desktop/src/lib/components/ui/calendar/index.ts new file mode 100644 index 0000000..f3a16d2 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/calendar/index.ts @@ -0,0 +1,40 @@ +import Root from "./calendar.svelte"; +import Cell from "./calendar-cell.svelte"; +import Day from "./calendar-day.svelte"; +import Grid from "./calendar-grid.svelte"; +import Header from "./calendar-header.svelte"; +import Months from "./calendar-months.svelte"; +import GridRow from "./calendar-grid-row.svelte"; +import Heading from "./calendar-heading.svelte"; +import GridBody from "./calendar-grid-body.svelte"; +import GridHead from "./calendar-grid-head.svelte"; +import HeadCell from "./calendar-head-cell.svelte"; +import NextButton from "./calendar-next-button.svelte"; +import PrevButton from "./calendar-prev-button.svelte"; +import MonthSelect from "./calendar-month-select.svelte"; +import YearSelect from "./calendar-year-select.svelte"; +import Month from "./calendar-month.svelte"; +import Nav from "./calendar-nav.svelte"; +import Caption from "./calendar-caption.svelte"; + +export { + Day, + Cell, + Grid, + Header, + Months, + GridRow, + Heading, + GridBody, + GridHead, + HeadCell, + NextButton, + PrevButton, + Nav, + Month, + YearSelect, + MonthSelect, + Caption, + // + Root as Calendar, +}; diff --git a/apps/desktop/src/lib/components/ui/card/card-action.svelte b/apps/desktop/src/lib/components/ui/card/card-action.svelte new file mode 100644 index 0000000..7c48844 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/card/card-action.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/card/card-content.svelte b/apps/desktop/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000..4f60ee3 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/card/card-description.svelte b/apps/desktop/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000..8366534 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,20 @@ + + +

    + {@render children?.()} +

    diff --git a/apps/desktop/src/lib/components/ui/card/card-footer.svelte b/apps/desktop/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000..17aa09a --- /dev/null +++ b/apps/desktop/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/card/card-header.svelte b/apps/desktop/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000..4ec42c5 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/card/card-title.svelte b/apps/desktop/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000..8c862d9 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/card/card.svelte b/apps/desktop/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000..86226bb --- /dev/null +++ b/apps/desktop/src/lib/components/ui/card/card.svelte @@ -0,0 +1,22 @@ + + +
    img:first-child]:pt-0 data-[size=sm]:gap-2 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-none *:[img:last-child]:rounded-none group/card flex flex-col", className)} + {...restProps} +> + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/card/index.ts b/apps/desktop/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000..4d3fce4 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/card/index.ts @@ -0,0 +1,25 @@ +import Root from "./card.svelte"; +import Content from "./card-content.svelte"; +import Description from "./card-description.svelte"; +import Footer from "./card-footer.svelte"; +import Header from "./card-header.svelte"; +import Title from "./card-title.svelte"; +import Action from "./card-action.svelte"; + +export { + Root, + Content, + Description, + Footer, + Header, + Title, + Action, + // + Root as Card, + Content as CardContent, + Description as CardDescription, + Footer as CardFooter, + Header as CardHeader, + Title as CardTitle, + Action as CardAction, +}; diff --git a/apps/desktop/src/lib/components/ui/carousel/carousel-content.svelte b/apps/desktop/src/lib/components/ui/carousel/carousel-content.svelte new file mode 100644 index 0000000..84c71f8 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/carousel/carousel-content.svelte @@ -0,0 +1,43 @@ + + +
    +
    + {@render children?.()} +
    +
    diff --git a/apps/desktop/src/lib/components/ui/carousel/carousel-item.svelte b/apps/desktop/src/lib/components/ui/carousel/carousel-item.svelte new file mode 100644 index 0000000..ebf1649 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/carousel/carousel-item.svelte @@ -0,0 +1,30 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/carousel/carousel-next.svelte b/apps/desktop/src/lib/components/ui/carousel/carousel-next.svelte new file mode 100644 index 0000000..4840386 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/carousel/carousel-next.svelte @@ -0,0 +1,39 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/carousel/carousel-previous.svelte b/apps/desktop/src/lib/components/ui/carousel/carousel-previous.svelte new file mode 100644 index 0000000..1c34aca --- /dev/null +++ b/apps/desktop/src/lib/components/ui/carousel/carousel-previous.svelte @@ -0,0 +1,39 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/carousel/carousel.svelte b/apps/desktop/src/lib/components/ui/carousel/carousel.svelte new file mode 100644 index 0000000..b3933b8 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/carousel/carousel.svelte @@ -0,0 +1,94 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/carousel/context.ts b/apps/desktop/src/lib/components/ui/carousel/context.ts new file mode 100644 index 0000000..a5fd74f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/carousel/context.ts @@ -0,0 +1,58 @@ +import type { WithElementRef } from "$lib/utils.js"; +import type { + EmblaCarouselSvelteType, + default as emblaCarouselSvelte, +} from "embla-carousel-svelte"; +import { getContext, hasContext, setContext } from "svelte"; +import type { HTMLAttributes } from "svelte/elements"; + +export type CarouselAPI = + NonNullable["on:emblaInit"]> extends ( + evt: CustomEvent + ) => void + ? CarouselAPI + : never; + +type EmblaCarouselConfig = NonNullable[1]>; + +export type CarouselOptions = EmblaCarouselConfig["options"]; +export type CarouselPlugins = EmblaCarouselConfig["plugins"]; + +//// + +export type CarouselProps = { + opts?: CarouselOptions; + plugins?: CarouselPlugins; + setApi?: (api: CarouselAPI | undefined) => void; + orientation?: "horizontal" | "vertical"; +} & WithElementRef>; + +const EMBLA_CAROUSEL_CONTEXT = Symbol("EMBLA_CAROUSEL_CONTEXT"); + +export type EmblaContext = { + api: CarouselAPI | undefined; + orientation: "horizontal" | "vertical"; + scrollNext: () => void; + scrollPrev: () => void; + canScrollNext: boolean; + canScrollPrev: boolean; + handleKeyDown: (e: KeyboardEvent) => void; + options: CarouselOptions; + plugins: CarouselPlugins; + onInit: (e: CustomEvent) => void; + scrollTo: (index: number, jump?: boolean) => void; + scrollSnaps: number[]; + selectedIndex: number; +}; + +export function setEmblaContext(config: EmblaContext): EmblaContext { + setContext(EMBLA_CAROUSEL_CONTEXT, config); + return config; +} + +export function getEmblaContext(name = "This component") { + if (!hasContext(EMBLA_CAROUSEL_CONTEXT)) { + throw new Error(`${name} must be used within a component`); + } + return getContext>(EMBLA_CAROUSEL_CONTEXT); +} diff --git a/apps/desktop/src/lib/components/ui/carousel/index.ts b/apps/desktop/src/lib/components/ui/carousel/index.ts new file mode 100644 index 0000000..957fc74 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/carousel/index.ts @@ -0,0 +1,19 @@ +import Root from "./carousel.svelte"; +import Content from "./carousel-content.svelte"; +import Item from "./carousel-item.svelte"; +import Previous from "./carousel-previous.svelte"; +import Next from "./carousel-next.svelte"; + +export { + Root, + Content, + Item, + Previous, + Next, + // + Root as Carousel, + Content as CarouselContent, + Item as CarouselItem, + Previous as CarouselPrevious, + Next as CarouselNext, +}; diff --git a/apps/desktop/src/lib/components/ui/chart/chart-container.svelte b/apps/desktop/src/lib/components/ui/chart/chart-container.svelte new file mode 100644 index 0000000..1eb8e39 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/chart/chart-container.svelte @@ -0,0 +1,80 @@ + + +
    + + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/chart/chart-style.svelte b/apps/desktop/src/lib/components/ui/chart/chart-style.svelte new file mode 100644 index 0000000..ea1b3b2 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/chart/chart-style.svelte @@ -0,0 +1,37 @@ + + +{#if themeContents} + {#key id} + + {themeContents} + + {/key} +{/if} diff --git a/apps/desktop/src/lib/components/ui/chart/chart-tooltip.svelte b/apps/desktop/src/lib/components/ui/chart/chart-tooltip.svelte new file mode 100644 index 0000000..e0da9ca --- /dev/null +++ b/apps/desktop/src/lib/components/ui/chart/chart-tooltip.svelte @@ -0,0 +1,184 @@ + + +{#snippet TooltipLabel()} + {#if formattedLabel} +
    + {#if typeof formattedLabel === "function"} + {@render formattedLabel()} + {:else} + {formattedLabel} + {/if} +
    + {/if} +{/snippet} + + +
    + {#if !nestLabel} + {@render TooltipLabel()} + {/if} +
    + {#each visibleSeries as item, i (item.key + i)} + {@const key = `${nameKey || item.key || item.label || "value"}`} + {@const itemConfig = getPayloadConfigFromPayload( + chart.config, + item, + key, + chartCtx.tooltip.data + )} + {@const indicatorColor = color || item.config?.color || item.color} +
    svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5", + indicator === "dot" && "items-center" + )} + > + {#if formatter && item.value !== undefined && item.label} + {@render formatter({ + value: item.value, + name: item.label, + item, + index: i, + payload: visibleSeries, + })} + {:else} + {#if itemConfig?.icon} + + {:else if !hideIndicator} +
    + {/if} +
    +
    + {#if nestLabel} + {@render TooltipLabel()} + {/if} + + {itemConfig?.label || item.label} + +
    + {#if item.value !== undefined} + + {item.value.toLocaleString()} + + {/if} +
    + {/if} +
    + {/each} +
    +
    +
    diff --git a/apps/desktop/src/lib/components/ui/chart/chart-utils.ts b/apps/desktop/src/lib/components/ui/chart/chart-utils.ts new file mode 100644 index 0000000..a289e35 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/chart/chart-utils.ts @@ -0,0 +1,68 @@ +import type { Tooltip } from "layerchart"; +import { getContext, setContext, type Component, type Snippet } from "svelte"; + +export const THEMES = { light: "", dark: ".dark" } as const; + +export type ChartConfig = { + [k in string]: { + label?: string; + icon?: Component; + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ); +}; + +export type ExtractSnippetParams = T extends Snippet<[infer P]> ? P : never; + +export type TooltipPayload = Tooltip.TooltipSeries; + +// Helper to extract item config from a payload. +export function getPayloadConfigFromPayload( + config: ChartConfig, + payload: TooltipPayload, + key: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data?: Record | null +) { + if (typeof payload !== "object" || payload === null) return undefined; + + const payloadConfig = + "config" in payload && typeof payload.config === "object" && payload.config !== null + ? payload.config + : undefined; + + let configLabelKey: string = key; + + if (payload.key === key) { + configLabelKey = payload.key; + } else if (payload.label === key) { + configLabelKey = payload.label; + } else if (key in payload && typeof payload[key as keyof typeof payload] === "string") { + configLabelKey = payload[key as keyof typeof payload] as string; + } else if ( + payloadConfig !== undefined && + key in payloadConfig && + typeof payloadConfig[key as keyof typeof payloadConfig] === "string" + ) { + configLabelKey = payloadConfig[key as keyof typeof payloadConfig] as string; + } else if (data != null && key in data && typeof data[key] === "string") { + configLabelKey = data[key] as string; + } + + return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config]; +} + +type ChartContextValue = { + config: ChartConfig; +}; + +const chartContextKey = Symbol("chart-context"); + +export function setChartContext(value: ChartContextValue) { + return setContext(chartContextKey, value); +} + +export function useChart() { + return getContext(chartContextKey); +} diff --git a/apps/desktop/src/lib/components/ui/chart/index.ts b/apps/desktop/src/lib/components/ui/chart/index.ts new file mode 100644 index 0000000..f22375e --- /dev/null +++ b/apps/desktop/src/lib/components/ui/chart/index.ts @@ -0,0 +1,6 @@ +import ChartContainer from "./chart-container.svelte"; +import ChartTooltip from "./chart-tooltip.svelte"; + +export { getPayloadConfigFromPayload, type ChartConfig } from "./chart-utils.js"; + +export { ChartContainer, ChartTooltip, ChartContainer as Container, ChartTooltip as Tooltip }; diff --git a/apps/desktop/src/lib/components/ui/checkbox/checkbox.svelte b/apps/desktop/src/lib/components/ui/checkbox/checkbox.svelte new file mode 100644 index 0000000..7ed665d --- /dev/null +++ b/apps/desktop/src/lib/components/ui/checkbox/checkbox.svelte @@ -0,0 +1,39 @@ + + + + {#snippet children({ checked, indeterminate })} +
    + {#if checked} + + {:else if indeterminate} + + {/if} +
    + {/snippet} +
    diff --git a/apps/desktop/src/lib/components/ui/checkbox/index.ts b/apps/desktop/src/lib/components/ui/checkbox/index.ts new file mode 100644 index 0000000..6d92d94 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/checkbox/index.ts @@ -0,0 +1,6 @@ +import Root from "./checkbox.svelte"; +export { + Root, + // + Root as Checkbox, +}; diff --git a/apps/desktop/src/lib/components/ui/collapsible/collapsible-content.svelte b/apps/desktop/src/lib/components/ui/collapsible/collapsible-content.svelte new file mode 100644 index 0000000..bdabb55 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/collapsible/collapsible-content.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/collapsible/collapsible-trigger.svelte b/apps/desktop/src/lib/components/ui/collapsible/collapsible-trigger.svelte new file mode 100644 index 0000000..ece7ad6 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/collapsible/collapsible-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/collapsible/collapsible.svelte b/apps/desktop/src/lib/components/ui/collapsible/collapsible.svelte new file mode 100644 index 0000000..39cdd4e --- /dev/null +++ b/apps/desktop/src/lib/components/ui/collapsible/collapsible.svelte @@ -0,0 +1,11 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/collapsible/index.ts b/apps/desktop/src/lib/components/ui/collapsible/index.ts new file mode 100644 index 0000000..169b479 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/collapsible/index.ts @@ -0,0 +1,13 @@ +import Root from "./collapsible.svelte"; +import Trigger from "./collapsible-trigger.svelte"; +import Content from "./collapsible-content.svelte"; + +export { + Root, + Content, + Trigger, + // + Root as Collapsible, + Content as CollapsibleContent, + Trigger as CollapsibleTrigger, +}; diff --git a/apps/desktop/src/lib/components/ui/command/command-dialog.svelte b/apps/desktop/src/lib/components/ui/command/command-dialog.svelte new file mode 100644 index 0000000..024c76c --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command-dialog.svelte @@ -0,0 +1,42 @@ + + + + + {title} + {description} + + + + + diff --git a/apps/desktop/src/lib/components/ui/command/command-empty.svelte b/apps/desktop/src/lib/components/ui/command/command-empty.svelte new file mode 100644 index 0000000..4cfd990 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command-empty.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/command/command-group.svelte b/apps/desktop/src/lib/components/ui/command/command-group.svelte new file mode 100644 index 0000000..0712eee --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command-group.svelte @@ -0,0 +1,32 @@ + + + + {#if heading} + + {heading} + + {/if} + + diff --git a/apps/desktop/src/lib/components/ui/command/command-input.svelte b/apps/desktop/src/lib/components/ui/command/command-input.svelte new file mode 100644 index 0000000..1c15468 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command-input.svelte @@ -0,0 +1,34 @@ + + +
    + + + {#snippet child({ props })} + + {/snippet} + + + + + +
    diff --git a/apps/desktop/src/lib/components/ui/command/command-item.svelte b/apps/desktop/src/lib/components/ui/command/command-item.svelte new file mode 100644 index 0000000..128ceb3 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command-item.svelte @@ -0,0 +1,25 @@ + + + + {@render children?.()} + + diff --git a/apps/desktop/src/lib/components/ui/command/command-link-item.svelte b/apps/desktop/src/lib/components/ui/command/command-link-item.svelte new file mode 100644 index 0000000..ada6d2c --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command-link-item.svelte @@ -0,0 +1,20 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/command/command-list.svelte b/apps/desktop/src/lib/components/ui/command/command-list.svelte new file mode 100644 index 0000000..e97747f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command-list.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/command/command-loading.svelte b/apps/desktop/src/lib/components/ui/command/command-loading.svelte new file mode 100644 index 0000000..19dd298 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command-loading.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/command/command-separator.svelte b/apps/desktop/src/lib/components/ui/command/command-separator.svelte new file mode 100644 index 0000000..35c4c95 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/command/command-shortcut.svelte b/apps/desktop/src/lib/components/ui/command/command-shortcut.svelte new file mode 100644 index 0000000..5322e6a --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/apps/desktop/src/lib/components/ui/command/command.svelte b/apps/desktop/src/lib/components/ui/command/command.svelte new file mode 100644 index 0000000..74a415b --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/command.svelte @@ -0,0 +1,25 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/command/index.ts b/apps/desktop/src/lib/components/ui/command/index.ts new file mode 100644 index 0000000..5435fbe --- /dev/null +++ b/apps/desktop/src/lib/components/ui/command/index.ts @@ -0,0 +1,37 @@ +import Root from "./command.svelte"; +import Loading from "./command-loading.svelte"; +import Dialog from "./command-dialog.svelte"; +import Empty from "./command-empty.svelte"; +import Group from "./command-group.svelte"; +import Item from "./command-item.svelte"; +import Input from "./command-input.svelte"; +import List from "./command-list.svelte"; +import Separator from "./command-separator.svelte"; +import Shortcut from "./command-shortcut.svelte"; +import LinkItem from "./command-link-item.svelte"; + +export { + Root, + Dialog, + Empty, + Group, + Item, + LinkItem, + Input, + List, + Separator, + Shortcut, + Loading, + // + Root as Command, + Dialog as CommandDialog, + Empty as CommandEmpty, + Group as CommandGroup, + Item as CommandItem, + LinkItem as CommandLinkItem, + Input as CommandInput, + List as CommandList, + Separator as CommandSeparator, + Shortcut as CommandShortcut, + Loading as CommandLoading, +}; diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-checkbox-item.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-checkbox-item.svelte new file mode 100644 index 0000000..71972e2 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-checkbox-item.svelte @@ -0,0 +1,41 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.()} + {/snippet} + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-content.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-content.svelte new file mode 100644 index 0000000..fb34a60 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-content.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-group-heading.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-group-heading.svelte new file mode 100644 index 0000000..0c0f459 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-group-heading.svelte @@ -0,0 +1,21 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-group.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-group.svelte new file mode 100644 index 0000000..c7c1e06 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-group.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-item.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-item.svelte new file mode 100644 index 0000000..d1601b6 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-item.svelte @@ -0,0 +1,27 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-label.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-label.svelte new file mode 100644 index 0000000..4b3b1fe --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-label.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-portal.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-portal.svelte new file mode 100644 index 0000000..96b1e3e --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-radio-group.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-radio-group.svelte new file mode 100644 index 0000000..964cb55 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-radio-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-radio-item.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-radio-item.svelte new file mode 100644 index 0000000..509d30d --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-radio-item.svelte @@ -0,0 +1,35 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.({ checked })} + {/snippet} + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-separator.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-separator.svelte new file mode 100644 index 0000000..e675367 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-shortcut.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-shortcut.svelte new file mode 100644 index 0000000..3305abc --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-sub-content.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-sub-content.svelte new file mode 100644 index 0000000..fd85c70 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-sub-content.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte new file mode 100644 index 0000000..a14cb6c --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-sub.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-sub.svelte new file mode 100644 index 0000000..a03827b --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-sub.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu-trigger.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu-trigger.svelte new file mode 100644 index 0000000..f6a1542 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu-trigger.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/context-menu.svelte b/apps/desktop/src/lib/components/ui/context-menu/context-menu.svelte new file mode 100644 index 0000000..cfaefb3 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/context-menu.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/context-menu/index.ts b/apps/desktop/src/lib/components/ui/context-menu/index.ts new file mode 100644 index 0000000..cbeaee1 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/context-menu/index.ts @@ -0,0 +1,52 @@ +import Root from "./context-menu.svelte"; +import Sub from "./context-menu-sub.svelte"; +import Portal from "./context-menu-portal.svelte"; +import Trigger from "./context-menu-trigger.svelte"; +import Group from "./context-menu-group.svelte"; +import RadioGroup from "./context-menu-radio-group.svelte"; +import Item from "./context-menu-item.svelte"; +import GroupHeading from "./context-menu-group-heading.svelte"; +import Content from "./context-menu-content.svelte"; +import Shortcut from "./context-menu-shortcut.svelte"; +import RadioItem from "./context-menu-radio-item.svelte"; +import Separator from "./context-menu-separator.svelte"; +import SubContent from "./context-menu-sub-content.svelte"; +import SubTrigger from "./context-menu-sub-trigger.svelte"; +import CheckboxItem from "./context-menu-checkbox-item.svelte"; +import Label from "./context-menu-label.svelte"; + +export { + Root, + Sub, + Portal, + Item, + GroupHeading, + Label, + Group, + Trigger, + Content, + Shortcut, + Separator, + RadioItem, + SubContent, + SubTrigger, + RadioGroup, + CheckboxItem, + // + Root as ContextMenu, + Sub as ContextMenuSub, + Portal as ContextMenuPortal, + Item as ContextMenuItem, + GroupHeading as ContextMenuGroupHeading, + Group as ContextMenuGroup, + Content as ContextMenuContent, + Trigger as ContextMenuTrigger, + Shortcut as ContextMenuShortcut, + RadioItem as ContextMenuRadioItem, + Separator as ContextMenuSeparator, + RadioGroup as ContextMenuRadioGroup, + SubContent as ContextMenuSubContent, + SubTrigger as ContextMenuSubTrigger, + CheckboxItem as ContextMenuCheckboxItem, + Label as ContextMenuLabel, +}; diff --git a/apps/desktop/src/lib/components/ui/copy-button/copy-button.svelte b/apps/desktop/src/lib/components/ui/copy-button/copy-button.svelte new file mode 100644 index 0000000..1ba0141 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/copy-button/copy-button.svelte @@ -0,0 +1,95 @@ + + + + + diff --git a/apps/desktop/src/lib/components/ui/copy-button/index.ts b/apps/desktop/src/lib/components/ui/copy-button/index.ts new file mode 100644 index 0000000..37158fa --- /dev/null +++ b/apps/desktop/src/lib/components/ui/copy-button/index.ts @@ -0,0 +1,3 @@ +import CopyButton from './copy-button.svelte'; + +export { CopyButton }; diff --git a/apps/desktop/src/lib/components/ui/data-table/data-table.svelte.ts b/apps/desktop/src/lib/components/ui/data-table/data-table.svelte.ts new file mode 100644 index 0000000..f3c30a7 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/data-table/data-table.svelte.ts @@ -0,0 +1,142 @@ +import { + type RowData, + type TableOptions, + type TableOptionsResolved, + type TableState, + type Updater, + createTable, +} from "@tanstack/table-core"; + +/** + * Creates a reactive TanStack table object for Svelte. + * @param options Table options to create the table with. + * @returns A reactive table object. + * @example + * ```svelte + * + * + * + * + * {#each table.getHeaderGroups() as headerGroup} + * + * {#each headerGroup.headers as header} + * + * {/each} + * + * {/each} + * + * + *
    + * + *
    + * ``` + */ +export function createSvelteTable(options: TableOptions) { + const resolvedOptions: TableOptionsResolved = mergeObjects( + { + state: {}, + onStateChange() {}, + renderFallbackValue: null, + mergeOptions: ( + defaultOptions: TableOptions, + options: Partial> + ) => { + return mergeObjects(defaultOptions, options); + }, + }, + options + ); + + const table = createTable(resolvedOptions); + let state = $state(table.initialState); + + function updateOptions() { + table.setOptions(() => { + return mergeObjects(resolvedOptions, options, { + state: mergeObjects(state, options.state || {}), + + onStateChange: (updater: Updater) => { + if (updater instanceof Function) state = updater(state); + else state = mergeObjects(state, updater); + + options.onStateChange?.(updater); + }, + }); + }); + } + + updateOptions(); + + $effect.pre(() => { + updateOptions(); + }); + + return table; +} + +type MaybeThunk = T | (() => T | null | undefined); +type Intersection = (T extends [infer H, ...infer R] + ? H & Intersection + : unknown) & {}; + +/** + * Lazily merges several objects (or thunks) while preserving + * getter semantics from every source. + * + * Proxy-based to avoid known WebKit recursion issue. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function mergeObjects[]>( + ...sources: Sources +): Intersection<{ [K in keyof Sources]: Sources[K] }> { + const resolve = (src: MaybeThunk): T | undefined => + typeof src === "function" ? (src() ?? undefined) : src; + + const findSourceWithKey = (key: PropertyKey) => { + for (let i = sources.length - 1; i >= 0; i--) { + const obj = resolve(sources[i]); + if (obj && key in obj) return obj; + } + return undefined; + }; + + return new Proxy(Object.create(null), { + get(_, key) { + const src = findSourceWithKey(key); + + return src?.[key as never]; + }, + + has(_, key) { + return !!findSourceWithKey(key); + }, + + ownKeys(): (string | symbol)[] { + // eslint-disable-next-line svelte/prefer-svelte-reactivity + const all = new Set(); + for (const s of sources) { + const obj = resolve(s); + if (obj) { + for (const k of Reflect.ownKeys(obj) as (string | symbol)[]) { + all.add(k); + } + } + } + return [...all]; + }, + + getOwnPropertyDescriptor(_, key) { + const src = findSourceWithKey(key); + if (!src) return undefined; + return { + configurable: true, + enumerable: true, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: (src as any)[key], + writable: true, + }; + }, + }) as Intersection<{ [K in keyof Sources]: Sources[K] }>; +} diff --git a/apps/desktop/src/lib/components/ui/data-table/flex-render.svelte b/apps/desktop/src/lib/components/ui/data-table/flex-render.svelte new file mode 100644 index 0000000..ac82a58 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/data-table/flex-render.svelte @@ -0,0 +1,40 @@ + + +{#if typeof content === "string"} + {content} +{:else if content instanceof Function} + + + {@const result = content(context as any)} + {#if result instanceof RenderComponentConfig} + {@const { component: Component, props } = result} + + {:else if result instanceof RenderSnippetConfig} + {@const { snippet, params } = result} + {@render snippet({ ...params, attach })} + {:else} + {result} + {/if} +{/if} diff --git a/apps/desktop/src/lib/components/ui/data-table/index.ts b/apps/desktop/src/lib/components/ui/data-table/index.ts new file mode 100644 index 0000000..5f4e77e --- /dev/null +++ b/apps/desktop/src/lib/components/ui/data-table/index.ts @@ -0,0 +1,3 @@ +export { default as FlexRender } from "./flex-render.svelte"; +export { renderComponent, renderSnippet } from "./render-helpers.js"; +export { createSvelteTable } from "./data-table.svelte.js"; diff --git a/apps/desktop/src/lib/components/ui/data-table/render-helpers.ts b/apps/desktop/src/lib/components/ui/data-table/render-helpers.ts new file mode 100644 index 0000000..fa036d6 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/data-table/render-helpers.ts @@ -0,0 +1,111 @@ +import type { Component, ComponentProps, Snippet } from "svelte"; + +/** + * A helper class to make it easy to identify Svelte components in + * `columnDef.cell` and `columnDef.header` properties. + * + * > NOTE: This class should only be used internally by the adapter. If you're + * reading this and you don't know what this is for, you probably don't need it. + * + * @example + * ```svelte + * {@const result = content(context as any)} + * {#if result instanceof RenderComponentConfig} + * {@const { component: Component, props } = result} + * + * {/if} + * ``` + */ +export class RenderComponentConfig { + component: TComponent; + props: ComponentProps | Record; + constructor( + component: TComponent, + props: ComponentProps | Record = {} + ) { + this.component = component; + this.props = props; + } +} + +/** + * A helper class to make it easy to identify Svelte Snippets in `columnDef.cell` and `columnDef.header` properties. + * + * > NOTE: This class should only be used internally by the adapter. If you're + * reading this and you don't know what this is for, you probably don't need it. + * + * @example + * ```svelte + * {@const result = content(context as any)} + * {#if result instanceof RenderSnippetConfig} + * {@const { snippet, params } = result} + * {@render snippet(params)} + * {/if} + * ``` + */ +export class RenderSnippetConfig { + snippet: Snippet<[TProps]>; + params: TProps; + constructor(snippet: Snippet<[TProps]>, params: TProps) { + this.snippet = snippet; + this.params = params; + } +} + +/** + * A helper function to help create cells from Svelte components through ColumnDef's `cell` and `header` properties. + * + * This is only to be used with Svelte Components - use `renderSnippet` for Svelte Snippets. + * + * @param component A Svelte component + * @param props The props to pass to `component` + * @returns A `RenderComponentConfig` object that helps svelte-table know how to render the header/cell component. + * @example + * ```ts + * // +page.svelte + * const defaultColumns = [ + * columnHelper.accessor('name', { + * header: header => renderComponent(SortHeader, { label: 'Name', header }), + * }), + * columnHelper.accessor('state', { + * header: header => renderComponent(SortHeader, { label: 'State', header }), + * }), + * ] + * ``` + * @see {@link https://tanstack.com/table/latest/docs/guide/column-defs} + */ +export function renderComponent< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + T extends Component, + Props extends ComponentProps, +>(component: T, props: Props = {} as Props) { + return new RenderComponentConfig(component, props); +} + +/** + * A helper function to help create cells from Svelte Snippets through ColumnDef's `cell` and `header` properties. + * + * The snippet must only take one parameter. + * + * This is only to be used with Snippets - use `renderComponent` for Svelte Components. + * + * @param snippet + * @param params + * @returns - A `RenderSnippetConfig` object that helps svelte-table know how to render the header/cell snippet. + * @example + * ```ts + * // +page.svelte + * const defaultColumns = [ + * columnHelper.accessor('name', { + * cell: cell => renderSnippet(nameSnippet, { name: cell.row.name }), + * }), + * columnHelper.accessor('state', { + * cell: cell => renderSnippet(stateSnippet, { state: cell.row.state }), + * }), + * ] + * ``` + * @see {@link https://tanstack.com/table/latest/docs/guide/column-defs} + */ +export function renderSnippet(snippet: Snippet<[TProps]>, params: TProps = {} as TProps) { + return new RenderSnippetConfig(snippet, params); +} diff --git a/apps/desktop/src/lib/components/ui/dialog/dialog-close.svelte b/apps/desktop/src/lib/components/ui/dialog/dialog-close.svelte new file mode 100644 index 0000000..de68f2f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/dialog-close.svelte @@ -0,0 +1,11 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dialog/dialog-content.svelte b/apps/desktop/src/lib/components/ui/dialog/dialog-content.svelte new file mode 100644 index 0000000..a66b54c --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/dialog-content.svelte @@ -0,0 +1,48 @@ + + + + + + {@render children?.()} + {#if showCloseButton} + + {#snippet child({ props })} + + {/snippet} + + {/if} + + diff --git a/apps/desktop/src/lib/components/ui/dialog/dialog-description.svelte b/apps/desktop/src/lib/components/ui/dialog/dialog-description.svelte new file mode 100644 index 0000000..bd220ac --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dialog/dialog-footer.svelte b/apps/desktop/src/lib/components/ui/dialog/dialog-footer.svelte new file mode 100644 index 0000000..2f54b2c --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/dialog-footer.svelte @@ -0,0 +1,32 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dialog/dialog-header.svelte b/apps/desktop/src/lib/components/ui/dialog/dialog-header.svelte new file mode 100644 index 0000000..8164f0f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/dialog-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/dialog/dialog-overlay.svelte b/apps/desktop/src/lib/components/ui/dialog/dialog-overlay.svelte new file mode 100644 index 0000000..19f69f0 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dialog/dialog-portal.svelte b/apps/desktop/src/lib/components/ui/dialog/dialog-portal.svelte new file mode 100644 index 0000000..ccfa79c --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/dialog-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dialog/dialog-title.svelte b/apps/desktop/src/lib/components/ui/dialog/dialog-title.svelte new file mode 100644 index 0000000..a17f573 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dialog/dialog-trigger.svelte b/apps/desktop/src/lib/components/ui/dialog/dialog-trigger.svelte new file mode 100644 index 0000000..589ee0c --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/dialog-trigger.svelte @@ -0,0 +1,11 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dialog/dialog.svelte b/apps/desktop/src/lib/components/ui/dialog/dialog.svelte new file mode 100644 index 0000000..211672c --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/dialog.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dialog/index.ts b/apps/desktop/src/lib/components/ui/dialog/index.ts new file mode 100644 index 0000000..076cef5 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dialog/index.ts @@ -0,0 +1,34 @@ +import Root from "./dialog.svelte"; +import Portal from "./dialog-portal.svelte"; +import Title from "./dialog-title.svelte"; +import Footer from "./dialog-footer.svelte"; +import Header from "./dialog-header.svelte"; +import Overlay from "./dialog-overlay.svelte"; +import Content from "./dialog-content.svelte"; +import Description from "./dialog-description.svelte"; +import Trigger from "./dialog-trigger.svelte"; +import Close from "./dialog-close.svelte"; + +export { + Root, + Title, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Close, + // + Root as Dialog, + Title as DialogTitle, + Portal as DialogPortal, + Footer as DialogFooter, + Header as DialogHeader, + Trigger as DialogTrigger, + Overlay as DialogOverlay, + Content as DialogContent, + Description as DialogDescription, + Close as DialogClose, +}; diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer-close.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer-close.svelte new file mode 100644 index 0000000..95c2479 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer-content.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer-content.svelte new file mode 100644 index 0000000..c389dd6 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer-content.svelte @@ -0,0 +1,33 @@ + + + + + + + {@render children?.()} + + diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer-description.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer-description.svelte new file mode 100644 index 0000000..ded26d2 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer-footer.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer-footer.svelte new file mode 100644 index 0000000..56a1fc6 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer-header.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer-header.svelte new file mode 100644 index 0000000..861f956 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer-nested.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer-nested.svelte new file mode 100644 index 0000000..834af94 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer-nested.svelte @@ -0,0 +1,12 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer-overlay.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer-overlay.svelte new file mode 100644 index 0000000..84df1e8 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer-overlay.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer-portal.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer-portal.svelte new file mode 100644 index 0000000..5a0dd74 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer-title.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer-title.svelte new file mode 100644 index 0000000..48c450e --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer-trigger.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer-trigger.svelte new file mode 100644 index 0000000..f1877d8 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/drawer/drawer.svelte b/apps/desktop/src/lib/components/ui/drawer/drawer.svelte new file mode 100644 index 0000000..0cb57ff --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/drawer.svelte @@ -0,0 +1,12 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/drawer/index.ts b/apps/desktop/src/lib/components/ui/drawer/index.ts new file mode 100644 index 0000000..1656cac --- /dev/null +++ b/apps/desktop/src/lib/components/ui/drawer/index.ts @@ -0,0 +1,38 @@ +import Root from "./drawer.svelte"; +import Content from "./drawer-content.svelte"; +import Description from "./drawer-description.svelte"; +import Overlay from "./drawer-overlay.svelte"; +import Footer from "./drawer-footer.svelte"; +import Header from "./drawer-header.svelte"; +import Title from "./drawer-title.svelte"; +import NestedRoot from "./drawer-nested.svelte"; +import Close from "./drawer-close.svelte"; +import Trigger from "./drawer-trigger.svelte"; +import Portal from "./drawer-portal.svelte"; + +export { + Root, + NestedRoot, + Content, + Description, + Overlay, + Footer, + Header, + Title, + Trigger, + Portal, + Close, + + // + Root as Drawer, + NestedRoot as DrawerNestedRoot, + Content as DrawerContent, + Description as DrawerDescription, + Overlay as DrawerOverlay, + Footer as DrawerFooter, + Header as DrawerHeader, + Title as DrawerTitle, + Trigger as DrawerTrigger, + Portal as DrawerPortal, + Close as DrawerClose, +}; diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte new file mode 100644 index 0000000..e0e1971 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte new file mode 100644 index 0000000..e56a65a --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte @@ -0,0 +1,44 @@ + + + + {#snippet children({ checked, indeterminate })} + + {#if indeterminate} + + {:else if checked} + + {/if} + + {@render childrenProp?.()} + {/snippet} + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte new file mode 100644 index 0000000..1442e33 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte @@ -0,0 +1,31 @@ + + + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte new file mode 100644 index 0000000..433540f --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte @@ -0,0 +1,22 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte new file mode 100644 index 0000000..aca1f7b --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte new file mode 100644 index 0000000..20713df --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte @@ -0,0 +1,27 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte new file mode 100644 index 0000000..05a9e64 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte new file mode 100644 index 0000000..274cfef --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte new file mode 100644 index 0000000..189aef4 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte new file mode 100644 index 0000000..e04807a --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte @@ -0,0 +1,34 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.({ checked })} + {/snippet} + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte new file mode 100644 index 0000000..45f82af --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte new file mode 100644 index 0000000..ed7cc85 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte new file mode 100644 index 0000000..8de5cf1 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte new file mode 100644 index 0000000..16f09e3 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte new file mode 100644 index 0000000..f044581 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte new file mode 100644 index 0000000..cb05344 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte new file mode 100644 index 0000000..cb4bc62 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/dropdown-menu/index.ts b/apps/desktop/src/lib/components/ui/dropdown-menu/index.ts new file mode 100644 index 0000000..7850c6a --- /dev/null +++ b/apps/desktop/src/lib/components/ui/dropdown-menu/index.ts @@ -0,0 +1,54 @@ +import Root from "./dropdown-menu.svelte"; +import Sub from "./dropdown-menu-sub.svelte"; +import CheckboxGroup from "./dropdown-menu-checkbox-group.svelte"; +import CheckboxItem from "./dropdown-menu-checkbox-item.svelte"; +import Content from "./dropdown-menu-content.svelte"; +import Group from "./dropdown-menu-group.svelte"; +import Item from "./dropdown-menu-item.svelte"; +import Label from "./dropdown-menu-label.svelte"; +import RadioGroup from "./dropdown-menu-radio-group.svelte"; +import RadioItem from "./dropdown-menu-radio-item.svelte"; +import Separator from "./dropdown-menu-separator.svelte"; +import Shortcut from "./dropdown-menu-shortcut.svelte"; +import Trigger from "./dropdown-menu-trigger.svelte"; +import SubContent from "./dropdown-menu-sub-content.svelte"; +import SubTrigger from "./dropdown-menu-sub-trigger.svelte"; +import GroupHeading from "./dropdown-menu-group-heading.svelte"; +import Portal from "./dropdown-menu-portal.svelte"; + +export { + CheckboxGroup, + CheckboxItem, + Content, + Portal, + Root as DropdownMenu, + CheckboxGroup as DropdownMenuCheckboxGroup, + CheckboxItem as DropdownMenuCheckboxItem, + Content as DropdownMenuContent, + Portal as DropdownMenuPortal, + Group as DropdownMenuGroup, + Item as DropdownMenuItem, + Label as DropdownMenuLabel, + RadioGroup as DropdownMenuRadioGroup, + RadioItem as DropdownMenuRadioItem, + Separator as DropdownMenuSeparator, + Shortcut as DropdownMenuShortcut, + Sub as DropdownMenuSub, + SubContent as DropdownMenuSubContent, + SubTrigger as DropdownMenuSubTrigger, + Trigger as DropdownMenuTrigger, + GroupHeading as DropdownMenuGroupHeading, + Group, + GroupHeading, + Item, + Label, + RadioGroup, + RadioItem, + Root, + Separator, + Shortcut, + Sub, + SubContent, + SubTrigger, + Trigger, +}; diff --git a/apps/desktop/src/lib/components/ui/empty/empty-content.svelte b/apps/desktop/src/lib/components/ui/empty/empty-content.svelte new file mode 100644 index 0000000..cc17823 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/empty/empty-content.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/empty/empty-description.svelte b/apps/desktop/src/lib/components/ui/empty/empty-description.svelte new file mode 100644 index 0000000..9e8cd78 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/empty/empty-description.svelte @@ -0,0 +1,23 @@ + + +
    a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4", + className + )} + {...restProps} +> + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/empty/empty-header.svelte b/apps/desktop/src/lib/components/ui/empty/empty-header.svelte new file mode 100644 index 0000000..7112f66 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/empty/empty-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/empty/empty-media.svelte b/apps/desktop/src/lib/components/ui/empty/empty-media.svelte new file mode 100644 index 0000000..24fe55d --- /dev/null +++ b/apps/desktop/src/lib/components/ui/empty/empty-media.svelte @@ -0,0 +1,41 @@ + + + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/empty/empty-title.svelte b/apps/desktop/src/lib/components/ui/empty/empty-title.svelte new file mode 100644 index 0000000..5e84e30 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/empty/empty-title.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/empty/empty.svelte b/apps/desktop/src/lib/components/ui/empty/empty.svelte new file mode 100644 index 0000000..e16c45c --- /dev/null +++ b/apps/desktop/src/lib/components/ui/empty/empty.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/empty/index.ts b/apps/desktop/src/lib/components/ui/empty/index.ts new file mode 100644 index 0000000..ae4c106 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/empty/index.ts @@ -0,0 +1,22 @@ +import Root from "./empty.svelte"; +import Header from "./empty-header.svelte"; +import Media from "./empty-media.svelte"; +import Title from "./empty-title.svelte"; +import Description from "./empty-description.svelte"; +import Content from "./empty-content.svelte"; + +export { + Root, + Header, + Media, + Title, + Description, + Content, + // + Root as Empty, + Header as EmptyHeader, + Media as EmptyMedia, + Title as EmptyTitle, + Description as EmptyDescription, + Content as EmptyContent, +}; diff --git a/apps/desktop/src/lib/components/ui/field/field-content.svelte b/apps/desktop/src/lib/components/ui/field/field-content.svelte new file mode 100644 index 0000000..1545c43 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/field-content.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/field/field-description.svelte b/apps/desktop/src/lib/components/ui/field/field-description.svelte new file mode 100644 index 0000000..f033c10 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/field-description.svelte @@ -0,0 +1,25 @@ + + +

    a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", + className + )} + {...restProps} +> + {@render children?.()} +

    diff --git a/apps/desktop/src/lib/components/ui/field/field-error.svelte b/apps/desktop/src/lib/components/ui/field/field-error.svelte new file mode 100644 index 0000000..326cf59 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/field-error.svelte @@ -0,0 +1,58 @@ + + +{#if hasContent} + +{/if} diff --git a/apps/desktop/src/lib/components/ui/field/field-group.svelte b/apps/desktop/src/lib/components/ui/field/field-group.svelte new file mode 100644 index 0000000..3795195 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/field-group.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/field/field-label.svelte b/apps/desktop/src/lib/components/ui/field/field-label.svelte new file mode 100644 index 0000000..8ccdbea --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/field-label.svelte @@ -0,0 +1,25 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/field/field-legend.svelte b/apps/desktop/src/lib/components/ui/field/field-legend.svelte new file mode 100644 index 0000000..e3d8deb --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/field-legend.svelte @@ -0,0 +1,24 @@ + + + + {@render children?.()} + diff --git a/apps/desktop/src/lib/components/ui/field/field-separator.svelte b/apps/desktop/src/lib/components/ui/field/field-separator.svelte new file mode 100644 index 0000000..a1ab858 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/field-separator.svelte @@ -0,0 +1,35 @@ + + +
    + + {#if children} + + {@render children()} + + {/if} +
    diff --git a/apps/desktop/src/lib/components/ui/field/field-set.svelte b/apps/desktop/src/lib/components/ui/field/field-set.svelte new file mode 100644 index 0000000..27ee330 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/field-set.svelte @@ -0,0 +1,20 @@ + + +
    [data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col", className)} + {...restProps} +> + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/field/field-title.svelte b/apps/desktop/src/lib/components/ui/field/field-title.svelte new file mode 100644 index 0000000..93c2dcf --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/field-title.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/field/field.svelte b/apps/desktop/src/lib/components/ui/field/field.svelte new file mode 100644 index 0000000..03d23c0 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/field.svelte @@ -0,0 +1,47 @@ + + + + +
    + {@render children?.()} +
    diff --git a/apps/desktop/src/lib/components/ui/field/index.ts b/apps/desktop/src/lib/components/ui/field/index.ts new file mode 100644 index 0000000..a644a95 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/field/index.ts @@ -0,0 +1,33 @@ +import Field from "./field.svelte"; +import Set from "./field-set.svelte"; +import Legend from "./field-legend.svelte"; +import Group from "./field-group.svelte"; +import Content from "./field-content.svelte"; +import Label from "./field-label.svelte"; +import Title from "./field-title.svelte"; +import Description from "./field-description.svelte"; +import Separator from "./field-separator.svelte"; +import Error from "./field-error.svelte"; + +export { + Field, + Set, + Legend, + Group, + Content, + Label, + Title, + Description, + Separator, + Error, + // + Set as FieldSet, + Legend as FieldLegend, + Group as FieldGroup, + Content as FieldContent, + Label as FieldLabel, + Title as FieldTitle, + Description as FieldDescription, + Separator as FieldSeparator, + Error as FieldError, +}; diff --git a/apps/desktop/src/lib/components/ui/form/form-button.svelte b/apps/desktop/src/lib/components/ui/form/form-button.svelte new file mode 100644 index 0000000..48d3936 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/form/form-button.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/input-group/input-group-input.svelte b/apps/desktop/src/lib/components/ui/input-group/input-group-input.svelte new file mode 100644 index 0000000..316c0cb --- /dev/null +++ b/apps/desktop/src/lib/components/ui/input-group/input-group-input.svelte @@ -0,0 +1,20 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/input-group/input-group-text.svelte b/apps/desktop/src/lib/components/ui/input-group/input-group-text.svelte new file mode 100644 index 0000000..d6ab315 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/input-group/input-group-text.svelte @@ -0,0 +1,19 @@ + + + + {@render children?.()} + diff --git a/apps/desktop/src/lib/components/ui/input-group/input-group-textarea.svelte b/apps/desktop/src/lib/components/ui/input-group/input-group-textarea.svelte new file mode 100644 index 0000000..9fbabe6 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/input-group/input-group-textarea.svelte @@ -0,0 +1,20 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/toggle-group/index.ts b/apps/desktop/src/lib/components/ui/toggle-group/index.ts new file mode 100644 index 0000000..12b14b9 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/toggle-group/index.ts @@ -0,0 +1,10 @@ +import Root from "./toggle-group.svelte"; +import Item from "./toggle-group-item.svelte"; + +export { + Root, + Item, + // + Root as ToggleGroup, + Item as ToggleGroupItem, +}; diff --git a/apps/desktop/src/lib/components/ui/toggle-group/toggle-group-item.svelte b/apps/desktop/src/lib/components/ui/toggle-group/toggle-group-item.svelte new file mode 100644 index 0000000..904bf70 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/toggle-group/toggle-group-item.svelte @@ -0,0 +1,35 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/toggle-group/toggle-group.svelte b/apps/desktop/src/lib/components/ui/toggle-group/toggle-group.svelte new file mode 100644 index 0000000..3d67cb8 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/toggle-group/toggle-group.svelte @@ -0,0 +1,75 @@ + + + + + + diff --git a/apps/desktop/src/lib/components/ui/toggle/index.ts b/apps/desktop/src/lib/components/ui/toggle/index.ts new file mode 100644 index 0000000..8cb2936 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/toggle/index.ts @@ -0,0 +1,13 @@ +import Root from "./toggle.svelte"; +export { + toggleVariants, + type ToggleSize, + type ToggleVariant, + type ToggleVariants, +} from "./toggle.svelte"; + +export { + Root, + // + Root as Toggle, +}; diff --git a/apps/desktop/src/lib/components/ui/toggle/toggle.svelte b/apps/desktop/src/lib/components/ui/toggle/toggle.svelte new file mode 100644 index 0000000..eca68b1 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/toggle/toggle.svelte @@ -0,0 +1,51 @@ + + + + + diff --git a/apps/desktop/src/lib/components/ui/tooltip/index.ts b/apps/desktop/src/lib/components/ui/tooltip/index.ts new file mode 100644 index 0000000..1718604 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/tooltip/index.ts @@ -0,0 +1,19 @@ +import Root from "./tooltip.svelte"; +import Trigger from "./tooltip-trigger.svelte"; +import Content from "./tooltip-content.svelte"; +import Provider from "./tooltip-provider.svelte"; +import Portal from "./tooltip-portal.svelte"; + +export { + Root, + Trigger, + Content, + Provider, + Portal, + // + Root as Tooltip, + Content as TooltipContent, + Trigger as TooltipTrigger, + Provider as TooltipProvider, + Portal as TooltipPortal, +}; diff --git a/apps/desktop/src/lib/components/ui/tooltip/tooltip-content.svelte b/apps/desktop/src/lib/components/ui/tooltip/tooltip-content.svelte new file mode 100644 index 0000000..4f27fa3 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/tooltip/tooltip-content.svelte @@ -0,0 +1,52 @@ + + + + + {@render children?.()} + + {#snippet child({ props })} +
    + {/snippet} +
    +
    +
    diff --git a/apps/desktop/src/lib/components/ui/tooltip/tooltip-portal.svelte b/apps/desktop/src/lib/components/ui/tooltip/tooltip-portal.svelte new file mode 100644 index 0000000..d234f7d --- /dev/null +++ b/apps/desktop/src/lib/components/ui/tooltip/tooltip-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/tooltip/tooltip-provider.svelte b/apps/desktop/src/lib/components/ui/tooltip/tooltip-provider.svelte new file mode 100644 index 0000000..6dba9a6 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/tooltip/tooltip-provider.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/tooltip/tooltip-trigger.svelte b/apps/desktop/src/lib/components/ui/tooltip/tooltip-trigger.svelte new file mode 100644 index 0000000..1acdaa4 --- /dev/null +++ b/apps/desktop/src/lib/components/ui/tooltip/tooltip-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/components/ui/tooltip/tooltip.svelte b/apps/desktop/src/lib/components/ui/tooltip/tooltip.svelte new file mode 100644 index 0000000..0b0f9ce --- /dev/null +++ b/apps/desktop/src/lib/components/ui/tooltip/tooltip.svelte @@ -0,0 +1,7 @@ + + + diff --git a/apps/desktop/src/lib/electrobun.ts b/apps/desktop/src/lib/electrobun.ts new file mode 100644 index 0000000..cae1e25 --- /dev/null +++ b/apps/desktop/src/lib/electrobun.ts @@ -0,0 +1,36 @@ +import { Electroview } from 'electrobun/view' + +type RPC = { + bun: { + requests: { + getSecret: { + params: { + service: string + name: string + } + response: Promise + } + setSecret: { + params: { + service: string + name: string + value: string + } + response: Promise + } + } + messages: {} + } + webview: { + requests: {} + messages: {} + } +} + +const rpc = Electroview.defineRPC({ + handlers: { + requests: {}, + messages: {}, + }, +}) +export const electrobun = new Electroview({ rpc }) diff --git a/apps/desktop/src/lib/hooks/is-mobile.svelte.ts b/apps/desktop/src/lib/hooks/is-mobile.svelte.ts new file mode 100644 index 0000000..4829c00 --- /dev/null +++ b/apps/desktop/src/lib/hooks/is-mobile.svelte.ts @@ -0,0 +1,9 @@ +import { MediaQuery } from "svelte/reactivity"; + +const DEFAULT_MOBILE_BREAKPOINT = 768; + +export class IsMobile extends MediaQuery { + constructor(breakpoint: number = DEFAULT_MOBILE_BREAKPOINT) { + super(`max-width: ${breakpoint - 1}px`); + } +} diff --git a/apps/desktop/src/lib/hooks/use-clipboard.svelte.ts b/apps/desktop/src/lib/hooks/use-clipboard.svelte.ts new file mode 100644 index 0000000..ab1b7d1 --- /dev/null +++ b/apps/desktop/src/lib/hooks/use-clipboard.svelte.ts @@ -0,0 +1,99 @@ +type Options = { + /** The time before the copied status is reset. */ + delay: number; +}; + +/** Use this hook to copy text to the clipboard and show a copied state. + * + * ## Usage + * ```svelte + * + * + * + * ``` + * + */ +export class UseClipboard { + #copiedStatus = $state<'success' | 'failure'>(); + private delay: number; + private timeout: ReturnType | undefined = undefined; + + constructor({ delay = 500 }: Partial = {}) { + this.delay = delay; + } + + /** Copies the given text to the users clipboard. + * + * ## Usage + * ```ts + * clipboard.copy('Hello, World!'); + * ``` + * + * @param text + * @returns + */ + async copy(text: string) { + if (this.timeout) { + this.#copiedStatus = undefined; + clearTimeout(this.timeout); + } + + this.#copiedStatus = await copyText(text); + + this.timeout = setTimeout(() => { + this.#copiedStatus = undefined; + }, this.delay); + + return this.#copiedStatus; + } + + /** true when the user has just copied to the clipboard. */ + get copied() { + return this.#copiedStatus === 'success'; + } + + /** Indicates whether a copy has occurred + * and gives a status of either `success` or `failure`. */ + get status() { + return this.#copiedStatus; + } +} + +export async function copyText(text: string): Promise<'success' | 'failure'> { + try { + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(text); + return 'success'; + } + + // when navigator.clipboard is unavailable we fallback to this for wider browser compatibility + const textArea = document.createElement('textarea'); + textArea.value = text; + textArea.style.position = 'fixed'; + textArea.style.top = '0'; + textArea.style.left = '0'; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + const successful = document.execCommand('copy'); + + document.body.removeChild(textArea); + + return successful ? 'success' : 'failure'; + } catch { + return 'failure'; + } +} diff --git a/apps/desktop/src/lib/states/wallet.svelte.ts b/apps/desktop/src/lib/states/wallet.svelte.ts new file mode 100644 index 0000000..68c066b --- /dev/null +++ b/apps/desktop/src/lib/states/wallet.svelte.ts @@ -0,0 +1,110 @@ +import { JsonRpcProvider, HDNodeWallet, formatEther } from 'ethers' +import { electrobun } from '$lib/electrobun' + +const BNB_RPC = 'https://bsc-dataseed.binance.org' + +export const Wallet = () => { + let seed = $state('') + let address = $state('') + let balance = $state('0') + let loading = $state(false) + let error = $state('') + let vaultExists = $state(false) + let ready = $state(false) + + const init = async () => { + try { + const raw = await electrobun.rpc?.request.getSecret({ + service: 'koins', + name: 'vault', + }) + vaultExists = raw !== null && raw !== undefined + if (raw) { + seed = raw + await refresh() + } + } catch (e) { + console.log(e) + error = + e instanceof Error ? e.message : 'Failed to access keychain' + } finally { + ready = true + } + } + + const saveVault = async (phrase: string) => { + loading = true + error = '' + try { + HDNodeWallet.fromPhrase(phrase.trim()) + await electrobun.rpc?.request.setSecret({ + service: 'koins', + name: 'vault', + value: phrase.trim(), + }) + seed = phrase.trim() + vaultExists = true + await refresh() + } catch (e) { + error = e instanceof Error ? e.message : 'Invalid seed phrase' + } finally { + loading = false + } + } + + const lock = () => { + seed = '' + address = '' + balance = '0' + error = '' + } + + const refresh = async () => { + if (!seed) return + loading = true + error = '' + try { + const wallet = HDNodeWallet.fromPhrase(seed) + address = wallet.address + const provider = new JsonRpcProvider(BNB_RPC) + const bal = await provider.getBalance(wallet.address) + balance = formatEther(bal) + } catch (e) { + error = + e instanceof Error ? e.message : 'Failed to fetch balance' + } finally { + loading = false + } + } + + return { + get isLocked() { + return vaultExists && !seed + }, + get seed() { + return seed + }, + get address() { + return address + }, + get balance() { + return balance + }, + get loading() { + return loading + }, + get error() { + return error + }, + get vaultExists() { + return vaultExists + }, + get ready() { + return ready + }, + refresh, + init, + lock, + saveVault, + } +} diff --git a/apps/desktop/src/lib/utils.ts b/apps/desktop/src/lib/utils.ts new file mode 100644 index 0000000..f92bfcb --- /dev/null +++ b/apps/desktop/src/lib/utils.ts @@ -0,0 +1,13 @@ +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChild = T extends { child?: any } ? Omit : T; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChildren = T extends { children?: any } ? Omit : T; +export type WithoutChildrenOrChild = WithoutChildren>; +export type WithElementRef = T & { ref?: U | null }; diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts new file mode 100644 index 0000000..121c709 --- /dev/null +++ b/apps/desktop/src/main.ts @@ -0,0 +1,6 @@ +import { mount } from 'svelte' +import App from './App.svelte' + +mount(App, { + target: document.querySelector('#app')!, +}) diff --git a/apps/desktop/src/routes/about.svelte b/apps/desktop/src/routes/about.svelte new file mode 100644 index 0000000..3f14839 --- /dev/null +++ b/apps/desktop/src/routes/about.svelte @@ -0,0 +1,5 @@ + + +

    About

    + +

    This is another page

    diff --git a/apps/desktop/src/routes/index.svelte b/apps/desktop/src/routes/index.svelte new file mode 100644 index 0000000..fe5f517 --- /dev/null +++ b/apps/desktop/src/routes/index.svelte @@ -0,0 +1,86 @@ + + +
    + {#if !w.ready} + + {:else if !w.vaultExists} + + + Import Wallet + + Enter your seed phrase to save it securely in your system + keychain + + + +
    +