diff --git a/ui-svelte/components.json b/ui-svelte/components.json
new file mode 100644
index 00000000..8bc41972
--- /dev/null
+++ b/ui-svelte/components.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://shadcn-svelte.com/schema.json",
+ "tailwind": {
+ "css": "src/index.css",
+ "baseColor": "zinc"
+ },
+ "aliases": {
+ "components": "$lib/components",
+ "utils": "$lib/utils",
+ "ui": "$lib/components/ui",
+ "hooks": "$lib/hooks",
+ "lib": "$lib"
+ },
+ "typescript": true,
+ "registry": "https://shadcn-svelte.com/registry",
+ "iconLibrary": "lucide"
+}
diff --git a/ui-svelte/package-lock.json b/ui-svelte/package-lock.json
index 01fc2229..09fa5323 100644
--- a/ui-svelte/package-lock.json
+++ b/ui-svelte/package-lock.json
@@ -23,14 +23,23 @@
"unist-util-visit": "^5.1.0"
},
"devDependencies": {
+ "@internationalized/date": "^3.12.2",
+ "@lucide/svelte": "^1.21.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tailwindcss/vite": "^4.1.8",
"@tsconfig/svelte": "^5.0.4",
"@types/hast": "^3.0.4",
"@types/node": "^25.1.0",
+ "bits-ui": "^2.18.1",
+ "clsx": "^2.1.1",
+ "paneforge": "^1.0.2",
+ "shadcn-svelte": "^1.3.0",
"svelte": "^5.46.4",
"svelte-check": "^4.1.4",
+ "tailwind-merge": "^3.6.0",
+ "tailwind-variants": "^3.2.2",
"tailwindcss": "^4.1.8",
+ "tw-animate-css": "^1.4.0",
"typescript": "~5.8.3",
"vite": "^8.0.0",
"vite-plugin-compression2": "^2.5.1",
@@ -71,6 +80,44 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
+ "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.11"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
+ "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.5",
+ "@floating-ui/utils": "^0.2.11"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
+ "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@internationalized/date": {
+ "version": "3.12.2",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.12.2.tgz",
+ "integrity": "sha512-FY1Y+H64NDs+HAF6omlnWxm3mEpfgaCSWtL5l551ZZfImA+kGjPFgrnJrGjH6lfmLL0g8Z/mBu1R3kufeCp6Jw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -127,6 +174,16 @@
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
"license": "MIT"
},
+ "node_modules/@lucide/svelte": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.21.0.tgz",
+ "integrity": "sha512-MEv//A7Jv3kHukZowv/DWp1MAtUzJKYwtJsmnQ7X98lCgtac3z3NbaToDl3Q6jO3gS9sougFpcD+t+YuxOkRMw==",
+ "dev": true,
+ "license": "ISC",
+ "peerDependencies": {
+ "svelte": "^5"
+ }
+ },
"node_modules/@napi-rs/wasm-runtime": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz",
@@ -480,6 +537,16 @@
"vite": "^8.0.0-beta.7 || ^8.0.0"
}
},
+ "node_modules/@swc/helpers": {
+ "version": "0.5.23",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz",
+ "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
"node_modules/@tailwindcss/node": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz",
@@ -1053,6 +1120,31 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/bits-ui": {
+ "version": "2.18.1",
+ "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.18.1.tgz",
+ "integrity": "sha512-KkemzKFH4T3gt3H+P86JcnAWExjByv/6vlwjm/BoCwTPHu03yiCdxbghdJLvFReQTe0acCAiRcKfmixxD6XvlA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.1",
+ "@floating-ui/dom": "^1.7.1",
+ "esm-env": "^1.1.2",
+ "runed": "^0.35.1",
+ "svelte-toolbelt": "^0.10.6",
+ "tabbable": "^6.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/huntabyte"
+ },
+ "peerDependencies": {
+ "@internationalized/date": "^3.8.1",
+ "svelte": "^5.33.0"
+ }
+ },
"node_modules/ccount": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
@@ -1555,6 +1647,13 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/inline-style-parser": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
+ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/is-plain-obj": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
@@ -1890,6 +1989,16 @@
"svelte": "^3 || ^4 || ^5.0.0-next.42"
}
},
+ "node_modules/lz-string": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
+ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "lz-string": "bin/bin.js"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -2756,6 +2865,13 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/node-fetch-native": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
+ "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/obug": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
@@ -2767,6 +2883,74 @@
],
"license": "MIT"
},
+ "node_modules/paneforge": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/paneforge/-/paneforge-1.0.2.tgz",
+ "integrity": "sha512-KzmIXQH1wCfwZ4RsMohD/IUtEjVhteR+c+ulb/CHYJHX8SuDXoJmChtsc/Xs5Wl8NHS4L5Q7cxL8MG40gSU1bA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "runed": "^0.23.4",
+ "svelte-toolbelt": "^0.9.2"
+ },
+ "peerDependencies": {
+ "svelte": "^5.29.0"
+ }
+ },
+ "node_modules/paneforge/node_modules/runed": {
+ "version": "0.23.4",
+ "resolved": "https://registry.npmjs.org/runed/-/runed-0.23.4.tgz",
+ "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/huntabyte",
+ "https://github.com/sponsors/tglide"
+ ],
+ "dependencies": {
+ "esm-env": "^1.0.0"
+ },
+ "peerDependencies": {
+ "svelte": "^5.7.0"
+ }
+ },
+ "node_modules/paneforge/node_modules/svelte-toolbelt": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.9.3.tgz",
+ "integrity": "sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/huntabyte"
+ ],
+ "dependencies": {
+ "clsx": "^2.1.1",
+ "runed": "^0.29.0",
+ "style-to-object": "^1.0.8"
+ },
+ "engines": {
+ "node": ">=18",
+ "pnpm": ">=8.7.0"
+ },
+ "peerDependencies": {
+ "svelte": "^5.30.2"
+ }
+ },
+ "node_modules/paneforge/node_modules/svelte-toolbelt/node_modules/runed": {
+ "version": "0.29.2",
+ "resolved": "https://registry.npmjs.org/runed/-/runed-0.29.2.tgz",
+ "integrity": "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/huntabyte",
+ "https://github.com/sponsors/tglide"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "esm-env": "^1.0.0"
+ },
+ "peerDependencies": {
+ "svelte": "^5.7.0"
+ }
+ },
"node_modules/parse5": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
@@ -3018,6 +3202,31 @@
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15"
}
},
+ "node_modules/runed": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmjs.org/runed/-/runed-0.35.1.tgz",
+ "integrity": "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/huntabyte",
+ "https://github.com/sponsors/tglide"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.3",
+ "esm-env": "^1.0.0",
+ "lz-string": "^1.5.0"
+ },
+ "peerDependencies": {
+ "@sveltejs/kit": "^2.21.0",
+ "svelte": "^5.7.0"
+ },
+ "peerDependenciesMeta": {
+ "@sveltejs/kit": {
+ "optional": true
+ }
+ }
+ },
"node_modules/sade": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
@@ -3031,6 +3240,35 @@
"node": ">=6"
}
},
+ "node_modules/shadcn-svelte": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/shadcn-svelte/-/shadcn-svelte-1.3.0.tgz",
+ "integrity": "sha512-Pd4ICWTkTks/b2YU4c9vF2XsX1x5HFPRl5bKszS1LcnWS83x+7T4WiIvbWz8Qh9knkcGZ+SCz1+Dmhdq+AYooA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^14.0.0",
+ "node-fetch-native": "^1.6.4",
+ "postcss": "^8.5.5",
+ "tailwind-merge": "^3.0.0"
+ },
+ "bin": {
+ "shadcn-svelte": "dist/index.mjs"
+ },
+ "peerDependencies": {
+ "svelte": "^5.0.0"
+ }
+ },
+ "node_modules/shadcn-svelte/node_modules/commander": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
"node_modules/siginfo": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
@@ -3086,6 +3324,16 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/style-to-object": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
+ "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inline-style-parser": "0.2.7"
+ }
+ },
"node_modules/svelte": {
"version": "5.55.7",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.7.tgz",
@@ -3150,6 +3398,65 @@
"url": "https://github.com/sponsors/ItalyPaleAle"
}
},
+ "node_modules/svelte-toolbelt": {
+ "version": "0.10.6",
+ "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.10.6.tgz",
+ "integrity": "sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/huntabyte"
+ ],
+ "dependencies": {
+ "clsx": "^2.1.1",
+ "runed": "^0.35.1",
+ "style-to-object": "^1.0.8"
+ },
+ "engines": {
+ "node": ">=18",
+ "pnpm": ">=8.7.0"
+ },
+ "peerDependencies": {
+ "svelte": "^5.30.2"
+ }
+ },
+ "node_modules/tabbable": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.5.0.tgz",
+ "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tailwind-merge": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz",
+ "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwind-variants": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz",
+ "integrity": "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.x",
+ "pnpm": ">=7.x"
+ },
+ "peerDependencies": {
+ "tailwind-merge": ">=3.0.0",
+ "tailwindcss": "*"
+ },
+ "peerDependenciesMeta": {
+ "tailwind-merge": {
+ "optional": true
+ }
+ }
+ },
"node_modules/tailwindcss": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz",
@@ -3247,8 +3554,17 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
- "license": "0BSD",
- "optional": true
+ "license": "0BSD"
+ },
+ "node_modules/tw-animate-css": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz",
+ "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Wombosvideo"
+ }
},
"node_modules/typescript": {
"version": "5.8.3",
diff --git a/ui-svelte/package.json b/ui-svelte/package.json
index efedc65b..2e73a344 100644
--- a/ui-svelte/package.json
+++ b/ui-svelte/package.json
@@ -12,20 +12,30 @@
"test:watch": "vitest"
},
"devDependencies": {
+ "@internationalized/date": "^3.12.2",
+ "@lucide/svelte": "^1.21.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tailwindcss/vite": "^4.1.8",
"@tsconfig/svelte": "^5.0.4",
"@types/hast": "^3.0.4",
"@types/node": "^25.1.0",
+ "bits-ui": "^2.18.1",
+ "clsx": "^2.1.1",
+ "paneforge": "^1.0.2",
+ "shadcn-svelte": "^1.3.0",
"svelte": "^5.46.4",
"svelte-check": "^4.1.4",
+ "tailwind-merge": "^3.6.0",
+ "tailwind-variants": "^3.2.2",
"tailwindcss": "^4.1.8",
+ "tw-animate-css": "^1.4.0",
"typescript": "~5.8.3",
"vite": "^8.0.0",
"vite-plugin-compression2": "^2.5.1",
"vitest": "^4.1.0"
},
"dependencies": {
+ "chart.js": "4.5.1",
"highlight.js": "^11.11.1",
"katex": "^0.16.28",
"lucide-svelte": "^0.563.0",
@@ -35,7 +45,6 @@
"remark-math": "^6.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.2",
- "chart.js": "4.5.1",
"svelte-spa-router": "^4.0.1",
"unified": "^11.0.5",
"unist-util-visit": "^5.1.0"
diff --git a/ui-svelte/src/App.svelte b/ui-svelte/src/App.svelte
index df2dc1a0..2e0d2620 100644
--- a/ui-svelte/src/App.svelte
+++ b/ui-svelte/src/App.svelte
@@ -27,7 +27,7 @@
}
$effect(() => {
- document.documentElement.setAttribute("data-theme", $isDarkMode ? "dark" : "light");
+ document.documentElement.classList.toggle("dark", $isDarkMode);
});
$effect(() => {
diff --git a/ui-svelte/src/index.css b/ui-svelte/src/index.css
index 1f833519..82ebd7dd 100644
--- a/ui-svelte/src/index.css
+++ b/ui-svelte/src/index.css
@@ -1,100 +1,184 @@
@import "tailwindcss";
+@import "tw-animate-css";
@import "katex/dist/katex.min.css";
-@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
-@theme {
- --color-background: rgba(252, 252, 249, 1);
- --color-surface: rgba(255, 255, 253, 1);
+@custom-variant dark (&:is(.dark *));
- /* text colors */
- --color-txtmain: rgba(19, 52, 59, 1);
- --color-txtsecondary: rgba(98, 108, 113, 1);
- --color-navlink-active: rgba(245, 245, 245, 1);
+:root {
+ --radius: 0.625rem;
- --color-primary: rgba(50, 184, 198, 1);
+ /* shadcn base palette (zinc) */
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.141 0.005 285.823);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.141 0.005 285.823);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.141 0.005 285.823);
- --color-primary-hover: rgba(29, 116, 128, 1);
- --color-primary-active: rgba(26, 104, 115, 1);
- --color-secondary: rgba(94, 82, 64, 0.12);
- --color-secondary-hover: rgba(94, 82, 64, 0.2);
- --color-secondary-active: rgba(94, 82, 64, 0.25);
- --color-border: rgba(94, 82, 64, 0.3);
- --color-btn-primary-text: rgba(252, 252, 249, 1);
- --color-card-border: rgba(94, 82, 64, 0.12);
- --color-card-border-inner: rgba(94, 82, 64, 0.12);
- --color-error: rgba(192, 21, 47, 1);
- --color-success: rgba(33, 128, 141, 1);
- --color-warning: rgb(244, 155, 0);
- --color-info: rgba(98, 108, 113, 1);
- --color-focus-ring: rgba(33, 128, 141, 0.4);
- --color-select-caret: rgba(19, 52, 59, 0.8);
- --color-btn-border: rgba(94, 82, 64, 0.7);
+ /* brand accent: llama-swap teal */
+ --primary: rgb(50 184 198);
+ --primary-foreground: oklch(0.985 0 0);
+
+ --secondary: oklch(0.967 0.001 286.375);
+ --secondary-foreground: oklch(0.21 0.006 285.885);
+ --muted: oklch(0.967 0.001 286.375);
+ --muted-foreground: oklch(0.552 0.016 285.938);
+ --accent: oklch(0.967 0.001 286.375);
+ --accent-foreground: oklch(0.21 0.006 285.885);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.92 0.004 286.32);
+ --input: oklch(0.92 0.004 286.32);
+ --ring: rgb(50 184 198);
+
+ --chart-1: rgb(50 184 198);
+ --chart-2: oklch(0.6 0.118 184.704);
+ --chart-3: oklch(0.398 0.07 227.392);
+ --chart-4: oklch(0.828 0.189 84.429);
+ --chart-5: oklch(0.769 0.188 70.08);
+
+ --sidebar: oklch(0.985 0 0);
+ --sidebar-foreground: oklch(0.141 0.005 285.823);
+ --sidebar-primary: rgb(50 184 198);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.967 0.001 286.375);
+ --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
+ --sidebar-border: oklch(0.92 0.004 286.32);
+ --sidebar-ring: rgb(50 184 198);
+
+ /* semantic status colors (shared light/dark-aware below) */
+ --success: oklch(0.6 0.118 184.704);
+ --warning: oklch(0.769 0.17 70.08);
+ --info: oklch(0.552 0.016 285.938);
}
-@layer theme {
- /* over ride theme for dark mode */
- [data-theme="dark"] {
- --color-background: rgba(31, 33, 33, 1);
- --color-surface: rgba(38, 40, 40, 1);
- /* text colors */
- --color-txtmain: rgba(245, 245, 245, 1);
- --color-txtsecondary: rgba(167, 169, 169, 0.7);
+.dark {
+ --background: oklch(0.141 0.005 285.823);
+ --foreground: oklch(0.985 0 0);
+ --card: oklch(0.21 0.006 285.885);
+ --card-foreground: oklch(0.985 0 0);
+ --popover: oklch(0.21 0.006 285.885);
+ --popover-foreground: oklch(0.985 0 0);
- --color-navlink-active: rgba(245, 245, 245, 1);
+ /* brand accent: deeper teal for dark surfaces */
+ --primary: rgb(45 166 178);
+ --primary-foreground: oklch(0.141 0.005 285.823);
- --color-primary: rgba(33, 128, 141, 1);
- --color-primary-hover: rgba(45, 166, 178, 1);
- --color-primary-active: rgba(41, 150, 161, 1);
- --color-secondary: rgba(119, 124, 124, 0.15);
- --color-secondary-hover: rgba(119, 124, 124, 0.25);
- --color-secondary-active: rgba(119, 124, 124, 0.3);
- --color-border: rgba(119, 124, 124, 0.3);
- --color-error: rgba(255, 84, 89, 1);
- --color-success: rgba(50, 184, 198, 1);
- --color-warning: rgb(244, 155, 0);
- --color-info: rgba(167, 169, 169, 1);
- --color-focus-ring: rgba(50, 184, 198, 0.4);
- --color-btn-primary-text: rgba(19, 52, 59, 1);
- --color-card-border: rgba(119, 124, 124, 0.2);
- --color-card-border-inner: rgba(119, 124, 124, 0.15);
- --shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- --button-border-secondary: rgba(119, 124, 124, 0.2);
- }
+ --secondary: oklch(0.274 0.006 286.033);
+ --secondary-foreground: oklch(0.985 0 0);
+ --muted: oklch(0.274 0.006 286.033);
+ --muted-foreground: oklch(0.705 0.015 286.067);
+ --accent: oklch(0.274 0.006 286.033);
+ --accent-foreground: oklch(0.985 0 0);
+ --destructive: oklch(0.704 0.191 22.216);
+ --border: oklch(1 0 0 / 10%);
+ --input: oklch(1 0 0 / 15%);
+ --ring: rgb(50 184 198);
+
+ --chart-1: rgb(50 184 198);
+ --chart-2: oklch(0.696 0.17 162.48);
+ --chart-3: oklch(0.769 0.188 70.08);
+ --chart-4: oklch(0.627 0.265 303.9);
+ --chart-5: oklch(0.645 0.246 16.439);
+
+ --sidebar: oklch(0.21 0.006 285.885);
+ --sidebar-foreground: oklch(0.985 0 0);
+ --sidebar-primary: rgb(50 184 198);
+ --sidebar-primary-foreground: oklch(0.141 0.005 285.823);
+ --sidebar-accent: oklch(0.274 0.006 286.033);
+ --sidebar-accent-foreground: oklch(0.985 0 0);
+ --sidebar-border: oklch(1 0 0 / 10%);
+ --sidebar-ring: rgb(50 184 198);
+
+ --success: oklch(0.696 0.17 162.48);
+ --warning: oklch(0.769 0.17 70.08);
+ --info: oklch(0.705 0.015 286.067);
+}
+
+@theme inline {
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --color-chart-1: var(--chart-1);
+ --color-chart-2: var(--chart-2);
+ --color-chart-3: var(--chart-3);
+ --color-chart-4: var(--chart-4);
+ --color-chart-5: var(--chart-5);
+ --color-sidebar: var(--sidebar);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-ring: var(--sidebar-ring);
+
+ --color-success: var(--success);
+ --color-warning: var(--warning);
+ --color-info: var(--info);
+ --color-error: var(--destructive);
+
+ /* legacy aliases (transitional — removed as views migrate) */
+ --color-surface: var(--card);
+ --color-txtmain: var(--foreground);
+ --color-txtsecondary: var(--muted-foreground);
+ --color-navlink-active: var(--primary-foreground);
+ --color-card-border: var(--border);
+ --color-card-border-inner: var(--border);
+ --color-btn-border: var(--border);
+ --color-btn-primary-text: var(--primary-foreground);
}
@layer base {
+ * {
+ @apply border-border outline-ring/50;
+ }
+
body {
- /* example of how colors using theme colors*/
- @apply bg-background text-txtmain;
+ @apply bg-background text-foreground;
}
h1 {
- @apply text-4xl text-txtmain font-bold pb-4;
+ @apply text-3xl font-bold tracking-tight pb-4;
}
h2 {
- @apply text-3xl text-txtmain font-bold pb-4;
+ @apply text-2xl font-bold tracking-tight pb-4;
}
h3 {
- @apply text-2xl text-txtmain font-bold pb-4;
+ @apply text-xl font-semibold tracking-tight pb-4;
}
h4 {
- @apply text-xl text-txtmain font-bold pb-4;
+ @apply text-lg font-semibold pb-4;
}
h5 {
- @apply text-lg text-txtmain font-bold pb-4;
+ @apply text-base font-semibold pb-4;
}
h6 {
- @apply text-base text-txtmain font-bold pb-4;
+ @apply text-sm font-semibold pb-4;
}
}
-/* define CSS classes here for specific types of components */
+/* Transitional component classes — kept until all views move to shadcn primitives. */
@layer components {
- .container {
- @apply px-4;
- }
-
- /* Tables */
table th {
@apply p-2 font-semibold;
}
@@ -102,77 +186,67 @@
@apply p-2;
}
- /* Navigation Header */
-
.navlink {
- @apply text-txtsecondary hover:bg-secondary hover:text-txtmain rounded-lg p-2;
+ @apply text-muted-foreground hover:bg-accent hover:text-foreground rounded-lg p-2;
}
.navlink.active {
- @apply bg-primary text-navlink-active;
+ @apply bg-primary text-primary-foreground;
}
- /* Card component */
.card {
- @apply bg-surface rounded-lg border border-card-border shadow-sm overflow-hidden p-4;
+ @apply bg-card text-card-foreground rounded-xl border shadow-sm overflow-hidden p-4;
}
-
- .card:hover {
- @apply shadow-md;
- }
-
.card__body {
@apply p-4;
}
-
.card__header,
.card__footer {
- @apply p-4 border-b border-card-border-inner;
+ @apply p-4 border-b;
}
- /* Status Badges */
.status {
- @apply inline-block px-2 py-1 text-xs font-medium rounded-lg;
+ @apply inline-flex items-center px-2 py-0.5 text-xs font-medium rounded-md;
}
-
.status--ready {
@apply bg-success/10 text-success;
}
-
.status--starting,
.status--stopping,
.status--queued {
@apply bg-warning/10 text-warning;
}
-
.status--stopped {
- @apply bg-error/10 text-error;
+ @apply bg-destructive/10 text-destructive;
}
- /* Buttons */
.btn {
- @apply bg-surface py-2 px-4 text-sm rounded-md border transition-colors duration-200 border-btn-border;
+ @apply bg-background py-2 px-4 text-sm rounded-md border transition-colors duration-200;
}
-
.btn:hover {
- cursor: pointer;
+ @apply bg-muted cursor-pointer;
}
-
.btn--sm {
@apply px-2 py-0.5 text-xs;
}
-
.btn:disabled {
@apply opacity-50 cursor-not-allowed;
}
}
-@layer utilities {
- .ml-2 {
- margin-left: 0.5rem;
- }
+@utility activity-link {
+ background: linear-gradient(90deg, #6366f1, #8b5cf6, #a855f7, #8b5cf6, #6366f1);
+ background-size: 200% 100%;
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ animation: gradient-shift 2s linear infinite;
+}
- .my-8 {
- margin-top: 2rem;
- margin-bottom: 2rem;
+@keyframes gradient-shift {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 100% {
+ background-position: 200% 50%;
}
}
diff --git a/ui-svelte/src/lib/components/ui/badge/badge.svelte b/ui-svelte/src/lib/components/ui/badge/badge.svelte
new file mode 100644
index 00000000..51bbc23e
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/badge/badge.svelte
@@ -0,0 +1,49 @@
+
+
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/badge/index.ts b/ui-svelte/src/lib/components/ui/badge/index.ts
new file mode 100644
index 00000000..64e0aa9b
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/components/ui/button/button.svelte b/ui-svelte/src/lib/components/ui/button/button.svelte
new file mode 100644
index 00000000..89cedcb7
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/button/button.svelte
@@ -0,0 +1,82 @@
+
+
+
+
+{#if href}
+
+ {@render children?.()}
+
+{:else}
+
+{/if}
diff --git a/ui-svelte/src/lib/components/ui/button/index.ts b/ui-svelte/src/lib/components/ui/button/index.ts
new file mode 100644
index 00000000..fb585d76
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/components/ui/card/card-action.svelte b/ui-svelte/src/lib/components/ui/card/card-action.svelte
new file mode 100644
index 00000000..7c48844a
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/card/card-action.svelte
@@ -0,0 +1,23 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/card/card-content.svelte b/ui-svelte/src/lib/components/ui/card/card-content.svelte
new file mode 100644
index 00000000..4f60ee3a
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/card/card-content.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/card/card-description.svelte b/ui-svelte/src/lib/components/ui/card/card-description.svelte
new file mode 100644
index 00000000..9b20ac70
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/card/card-description.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/card/card-footer.svelte b/ui-svelte/src/lib/components/ui/card/card-footer.svelte
new file mode 100644
index 00000000..1efff6d9
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/card/card-footer.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/card/card-header.svelte b/ui-svelte/src/lib/components/ui/card/card-header.svelte
new file mode 100644
index 00000000..7eb69e12
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/card/card-header.svelte
@@ -0,0 +1,23 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/card/card-title.svelte b/ui-svelte/src/lib/components/ui/card/card-title.svelte
new file mode 100644
index 00000000..1523fc52
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/card/card-title.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/card/card.svelte b/ui-svelte/src/lib/components/ui/card/card.svelte
new file mode 100644
index 00000000..2b4f81af
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/card/card.svelte
@@ -0,0 +1,22 @@
+
+
+img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)}
+ {...restProps}
+>
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/card/index.ts b/ui-svelte/src/lib/components/ui/card/index.ts
new file mode 100644
index 00000000..4d3fce48
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/components/ui/dialog/dialog-close.svelte b/ui-svelte/src/lib/components/ui/dialog/dialog-close.svelte
new file mode 100644
index 00000000..de68f2f0
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dialog/dialog-close.svelte
@@ -0,0 +1,11 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dialog/dialog-content.svelte b/ui-svelte/src/lib/components/ui/dialog/dialog-content.svelte
new file mode 100644
index 00000000..c0551ee3
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dialog/dialog-content.svelte
@@ -0,0 +1,48 @@
+
+
+
+
+
+ {@render children?.()}
+ {#if showCloseButton}
+
+ {#snippet child({ props })}
+
+ {/snippet}
+
+ {/if}
+
+
diff --git a/ui-svelte/src/lib/components/ui/dialog/dialog-description.svelte b/ui-svelte/src/lib/components/ui/dialog/dialog-description.svelte
new file mode 100644
index 00000000..0102d910
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dialog/dialog-description.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dialog/dialog-footer.svelte b/ui-svelte/src/lib/components/ui/dialog/dialog-footer.svelte
new file mode 100644
index 00000000..e23160ae
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dialog/dialog-footer.svelte
@@ -0,0 +1,32 @@
+
+
+
+ {@render children?.()}
+ {#if showCloseButton}
+
+ {#snippet child({ props })}
+
+ {/snippet}
+
+ {/if}
+
diff --git a/ui-svelte/src/lib/components/ui/dialog/dialog-header.svelte b/ui-svelte/src/lib/components/ui/dialog/dialog-header.svelte
new file mode 100644
index 00000000..c3ce8a24
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dialog/dialog-header.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/dialog/dialog-overlay.svelte b/ui-svelte/src/lib/components/ui/dialog/dialog-overlay.svelte
new file mode 100644
index 00000000..19f69f05
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dialog/dialog-overlay.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dialog/dialog-portal.svelte b/ui-svelte/src/lib/components/ui/dialog/dialog-portal.svelte
new file mode 100644
index 00000000..ccfa79ca
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dialog/dialog-portal.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dialog/dialog-title.svelte b/ui-svelte/src/lib/components/ui/dialog/dialog-title.svelte
new file mode 100644
index 00000000..6ff1a4a5
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dialog/dialog-title.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dialog/dialog-trigger.svelte b/ui-svelte/src/lib/components/ui/dialog/dialog-trigger.svelte
new file mode 100644
index 00000000..589ee0c3
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dialog/dialog-trigger.svelte
@@ -0,0 +1,11 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dialog/dialog.svelte b/ui-svelte/src/lib/components/ui/dialog/dialog.svelte
new file mode 100644
index 00000000..211672c6
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dialog/dialog.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dialog/index.ts b/ui-svelte/src/lib/components/ui/dialog/index.ts
new file mode 100644
index 00000000..076cef52
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte
new file mode 100644
index 00000000..e0e19718
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte
@@ -0,0 +1,16 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
new file mode 100644
index 00000000..a81b48d0
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
new file mode 100644
index 00000000..261a5b08
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte
new file mode 100644
index 00000000..433540fd
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte
@@ -0,0 +1,22 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte
new file mode 100644
index 00000000..aca1f7bd
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte
new file mode 100644
index 00000000..c425190d
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte
@@ -0,0 +1,27 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte
new file mode 100644
index 00000000..e0c534fe
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte
@@ -0,0 +1,24 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte
new file mode 100644
index 00000000..274cfef7
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
new file mode 100644
index 00000000..189aef40
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
@@ -0,0 +1,16 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte
new file mode 100644
index 00000000..c8aa07b1
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte
new file mode 100644
index 00000000..90f1b6f1
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
new file mode 100644
index 00000000..ed7cc85a
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
new file mode 100644
index 00000000..b5750d4d
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
new file mode 100644
index 00000000..fab0275d
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
@@ -0,0 +1,29 @@
+
+
+
+ {@render children?.()}
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte
new file mode 100644
index 00000000..f0445813
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte
new file mode 100644
index 00000000..cb053444
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte
new file mode 100644
index 00000000..cb4bc621
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/dropdown-menu/index.ts b/ui-svelte/src/lib/components/ui/dropdown-menu/index.ts
new file mode 100644
index 00000000..7850c6a3
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/components/ui/input/index.ts b/ui-svelte/src/lib/components/ui/input/index.ts
new file mode 100644
index 00000000..f47b6d3f
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/input/index.ts
@@ -0,0 +1,7 @@
+import Root from "./input.svelte";
+
+export {
+ Root,
+ //
+ Root as Input,
+};
diff --git a/ui-svelte/src/lib/components/ui/input/input.svelte b/ui-svelte/src/lib/components/ui/input/input.svelte
new file mode 100644
index 00000000..fe7db38f
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/input/input.svelte
@@ -0,0 +1,48 @@
+
+
+{#if type === "file"}
+
+{:else}
+
+{/if}
diff --git a/ui-svelte/src/lib/components/ui/label/index.ts b/ui-svelte/src/lib/components/ui/label/index.ts
new file mode 100644
index 00000000..8bfca0b3
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/label/index.ts
@@ -0,0 +1,7 @@
+import Root from "./label.svelte";
+
+export {
+ Root,
+ //
+ Root as Label,
+};
diff --git a/ui-svelte/src/lib/components/ui/label/label.svelte b/ui-svelte/src/lib/components/ui/label/label.svelte
new file mode 100644
index 00000000..d5e30863
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/label/label.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/resizable/index.ts b/ui-svelte/src/lib/components/ui/resizable/index.ts
new file mode 100644
index 00000000..2e37f114
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/resizable/index.ts
@@ -0,0 +1,13 @@
+import { Pane } from "paneforge";
+import Handle from "./resizable-handle.svelte";
+import PaneGroup from "./resizable-pane-group.svelte";
+
+export {
+ PaneGroup,
+ Pane,
+ Handle,
+ //
+ PaneGroup as ResizablePaneGroup,
+ Pane as ResizablePane,
+ Handle as ResizableHandle,
+};
diff --git a/ui-svelte/src/lib/components/ui/resizable/resizable-handle.svelte b/ui-svelte/src/lib/components/ui/resizable/resizable-handle.svelte
new file mode 100644
index 00000000..4650600a
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/resizable/resizable-handle.svelte
@@ -0,0 +1,27 @@
+
+
+div]:rotate-90",
+ className
+ )}
+ {...restProps}
+>
+ {#if withHandle}
+
+ {/if}
+
diff --git a/ui-svelte/src/lib/components/ui/resizable/resizable-pane-group.svelte b/ui-svelte/src/lib/components/ui/resizable/resizable-pane-group.svelte
new file mode 100644
index 00000000..4b1801da
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/resizable/resizable-pane-group.svelte
@@ -0,0 +1,24 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/scroll-area/index.ts b/ui-svelte/src/lib/components/ui/scroll-area/index.ts
new file mode 100644
index 00000000..e86a25b2
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/scroll-area/index.ts
@@ -0,0 +1,10 @@
+import Scrollbar from "./scroll-area-scrollbar.svelte";
+import Root from "./scroll-area.svelte";
+
+export {
+ Root,
+ Scrollbar,
+ //,
+ Root as ScrollArea,
+ Scrollbar as ScrollAreaScrollbar,
+};
diff --git a/ui-svelte/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte b/ui-svelte/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte
new file mode 100644
index 00000000..b9518f36
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte
@@ -0,0 +1,30 @@
+
+
+
+ {@render children?.()}
+
+
diff --git a/ui-svelte/src/lib/components/ui/scroll-area/scroll-area.svelte b/ui-svelte/src/lib/components/ui/scroll-area/scroll-area.svelte
new file mode 100644
index 00000000..d4d96d0f
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/scroll-area/scroll-area.svelte
@@ -0,0 +1,43 @@
+
+
+
+
+ {@render children?.()}
+
+ {#if orientation === "vertical" || orientation === "both"}
+
+ {/if}
+ {#if orientation === "horizontal" || orientation === "both"}
+
+ {/if}
+
+
diff --git a/ui-svelte/src/lib/components/ui/select/index.ts b/ui-svelte/src/lib/components/ui/select/index.ts
new file mode 100644
index 00000000..4dec358b
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/index.ts
@@ -0,0 +1,37 @@
+import Root from "./select.svelte";
+import Group from "./select-group.svelte";
+import Label from "./select-label.svelte";
+import Item from "./select-item.svelte";
+import Content from "./select-content.svelte";
+import Trigger from "./select-trigger.svelte";
+import Separator from "./select-separator.svelte";
+import ScrollDownButton from "./select-scroll-down-button.svelte";
+import ScrollUpButton from "./select-scroll-up-button.svelte";
+import GroupHeading from "./select-group-heading.svelte";
+import Portal from "./select-portal.svelte";
+
+export {
+ Root,
+ Group,
+ Label,
+ Item,
+ Content,
+ Trigger,
+ Separator,
+ ScrollDownButton,
+ ScrollUpButton,
+ GroupHeading,
+ Portal,
+ //
+ Root as Select,
+ Group as SelectGroup,
+ Label as SelectLabel,
+ Item as SelectItem,
+ Content as SelectContent,
+ Trigger as SelectTrigger,
+ Separator as SelectSeparator,
+ ScrollDownButton as SelectScrollDownButton,
+ ScrollUpButton as SelectScrollUpButton,
+ GroupHeading as SelectGroupHeading,
+ Portal as SelectPortal,
+};
diff --git a/ui-svelte/src/lib/components/ui/select/select-content.svelte b/ui-svelte/src/lib/components/ui/select/select-content.svelte
new file mode 100644
index 00000000..bb1e2ac2
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select-content.svelte
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ {@render children?.()}
+
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/select/select-group-heading.svelte b/ui-svelte/src/lib/components/ui/select/select-group-heading.svelte
new file mode 100644
index 00000000..1fab5f00
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select-group-heading.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/select/select-group.svelte b/ui-svelte/src/lib/components/ui/select/select-group.svelte
new file mode 100644
index 00000000..f666cb22
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select-group.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/select/select-item.svelte b/ui-svelte/src/lib/components/ui/select/select-item.svelte
new file mode 100644
index 00000000..e4a53d29
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select-item.svelte
@@ -0,0 +1,40 @@
+
+
+
+ {#snippet children({ selected, highlighted })}
+
+ {#if selected}
+
+ {/if}
+
+
+ {#if childrenProp}
+ {@render childrenProp({ selected, highlighted })}
+ {:else}
+ {label || value}
+ {/if}
+
+ {/snippet}
+
diff --git a/ui-svelte/src/lib/components/ui/select/select-label.svelte b/ui-svelte/src/lib/components/ui/select/select-label.svelte
new file mode 100644
index 00000000..69bcfdfe
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select-label.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/select/select-portal.svelte b/ui-svelte/src/lib/components/ui/select/select-portal.svelte
new file mode 100644
index 00000000..424bcddc
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select-portal.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/select/select-scroll-down-button.svelte b/ui-svelte/src/lib/components/ui/select/select-scroll-down-button.svelte
new file mode 100644
index 00000000..94f41cdd
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select-scroll-down-button.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/select/select-scroll-up-button.svelte b/ui-svelte/src/lib/components/ui/select/select-scroll-up-button.svelte
new file mode 100644
index 00000000..035ea094
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select-scroll-up-button.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/select/select-separator.svelte b/ui-svelte/src/lib/components/ui/select/select-separator.svelte
new file mode 100644
index 00000000..3b24bab6
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select-separator.svelte
@@ -0,0 +1,18 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/select/select-trigger.svelte b/ui-svelte/src/lib/components/ui/select/select-trigger.svelte
new file mode 100644
index 00000000..03d06d0f
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select-trigger.svelte
@@ -0,0 +1,29 @@
+
+
+
+ {@render children?.()}
+
+
diff --git a/ui-svelte/src/lib/components/ui/select/select.svelte b/ui-svelte/src/lib/components/ui/select/select.svelte
new file mode 100644
index 00000000..05eb6634
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/select/select.svelte
@@ -0,0 +1,11 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/separator/index.ts b/ui-svelte/src/lib/components/ui/separator/index.ts
new file mode 100644
index 00000000..82442d2c
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/separator/index.ts
@@ -0,0 +1,7 @@
+import Root from "./separator.svelte";
+
+export {
+ Root,
+ //
+ Root as Separator,
+};
diff --git a/ui-svelte/src/lib/components/ui/separator/separator.svelte b/ui-svelte/src/lib/components/ui/separator/separator.svelte
new file mode 100644
index 00000000..5fd8a429
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/separator/separator.svelte
@@ -0,0 +1,23 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sheet/index.ts b/ui-svelte/src/lib/components/ui/sheet/index.ts
new file mode 100644
index 00000000..28d7da1c
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/index.ts
@@ -0,0 +1,34 @@
+import Root from "./sheet.svelte";
+import Portal from "./sheet-portal.svelte";
+import Trigger from "./sheet-trigger.svelte";
+import Close from "./sheet-close.svelte";
+import Overlay from "./sheet-overlay.svelte";
+import Content from "./sheet-content.svelte";
+import Header from "./sheet-header.svelte";
+import Footer from "./sheet-footer.svelte";
+import Title from "./sheet-title.svelte";
+import Description from "./sheet-description.svelte";
+
+export {
+ Root,
+ Close,
+ Trigger,
+ Portal,
+ Overlay,
+ Content,
+ Header,
+ Footer,
+ Title,
+ Description,
+ //
+ Root as Sheet,
+ Close as SheetClose,
+ Trigger as SheetTrigger,
+ Portal as SheetPortal,
+ Overlay as SheetOverlay,
+ Content as SheetContent,
+ Header as SheetHeader,
+ Footer as SheetFooter,
+ Title as SheetTitle,
+ Description as SheetDescription,
+};
diff --git a/ui-svelte/src/lib/components/ui/sheet/sheet-close.svelte b/ui-svelte/src/lib/components/ui/sheet/sheet-close.svelte
new file mode 100644
index 00000000..ae382c12
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/sheet-close.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sheet/sheet-content.svelte b/ui-svelte/src/lib/components/ui/sheet/sheet-content.svelte
new file mode 100644
index 00000000..20957dcc
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/sheet-content.svelte
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+ {@render children?.()}
+ {#if showCloseButton}
+
+ {#snippet child({ props })}
+
+ {/snippet}
+
+ {/if}
+
+
diff --git a/ui-svelte/src/lib/components/ui/sheet/sheet-description.svelte b/ui-svelte/src/lib/components/ui/sheet/sheet-description.svelte
new file mode 100644
index 00000000..333b17a7
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/sheet-description.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sheet/sheet-footer.svelte b/ui-svelte/src/lib/components/ui/sheet/sheet-footer.svelte
new file mode 100644
index 00000000..ad9e07bb
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/sheet-footer.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sheet/sheet-header.svelte b/ui-svelte/src/lib/components/ui/sheet/sheet-header.svelte
new file mode 100644
index 00000000..504465f9
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/sheet-header.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sheet/sheet-overlay.svelte b/ui-svelte/src/lib/components/ui/sheet/sheet-overlay.svelte
new file mode 100644
index 00000000..8d338474
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/sheet-overlay.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sheet/sheet-portal.svelte b/ui-svelte/src/lib/components/ui/sheet/sheet-portal.svelte
new file mode 100644
index 00000000..f3085a3c
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/sheet-portal.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sheet/sheet-title.svelte b/ui-svelte/src/lib/components/ui/sheet/sheet-title.svelte
new file mode 100644
index 00000000..10c17178
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/sheet-title.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sheet/sheet-trigger.svelte b/ui-svelte/src/lib/components/ui/sheet/sheet-trigger.svelte
new file mode 100644
index 00000000..e266975f
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/sheet-trigger.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sheet/sheet.svelte b/ui-svelte/src/lib/components/ui/sheet/sheet.svelte
new file mode 100644
index 00000000..5bf97839
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sheet/sheet.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/constants.ts b/ui-svelte/src/lib/components/ui/sidebar/constants.ts
new file mode 100644
index 00000000..1347e83c
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/constants.ts
@@ -0,0 +1,6 @@
+export const SIDEBAR_COOKIE_NAME = "sidebar_state";
+export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
+export const SIDEBAR_WIDTH = "16rem";
+export const SIDEBAR_WIDTH_MOBILE = "18rem";
+export const SIDEBAR_WIDTH_ICON = "3rem";
+export const SIDEBAR_KEYBOARD_SHORTCUT = "b";
diff --git a/ui-svelte/src/lib/components/ui/sidebar/context.svelte.ts b/ui-svelte/src/lib/components/ui/sidebar/context.svelte.ts
new file mode 100644
index 00000000..15248ada
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/context.svelte.ts
@@ -0,0 +1,81 @@
+import { IsMobile } from "$lib/hooks/is-mobile.svelte.js";
+import { getContext, setContext } from "svelte";
+import { SIDEBAR_KEYBOARD_SHORTCUT } from "./constants.js";
+
+type Getter = () => T;
+
+export type SidebarStateProps = {
+ /**
+ * A getter function that returns the current open state of the sidebar.
+ * We use a getter function here to support `bind:open` on the `Sidebar.Provider`
+ * component.
+ */
+ open: Getter;
+
+ /**
+ * A function that sets the open state of the sidebar. To support `bind:open`, we need
+ * a source of truth for changing the open state to ensure it will be synced throughout
+ * the sub-components and any `bind:` references.
+ */
+ setOpen: (open: boolean) => void;
+};
+
+class SidebarState {
+ readonly props: SidebarStateProps;
+ open = $derived.by(() => this.props.open());
+ openMobile = $state(false);
+ setOpen: SidebarStateProps["setOpen"];
+ #isMobile: IsMobile;
+ state = $derived.by(() => (this.open ? "expanded" : "collapsed"));
+
+ constructor(props: SidebarStateProps) {
+ this.setOpen = props.setOpen;
+ this.#isMobile = new IsMobile();
+ this.props = props;
+ }
+
+ // Convenience getter for checking if the sidebar is mobile
+ // without this, we would need to use `sidebar.isMobile.current` everywhere
+ get isMobile() {
+ return this.#isMobile.current;
+ }
+
+ // Event handler to apply to the ``
+ handleShortcutKeydown = (e: KeyboardEvent) => {
+ if (e.key === SIDEBAR_KEYBOARD_SHORTCUT && (e.metaKey || e.ctrlKey)) {
+ e.preventDefault();
+ this.toggle();
+ }
+ };
+
+ setOpenMobile = (value: boolean) => {
+ this.openMobile = value;
+ };
+
+ toggle = () => {
+ return this.#isMobile.current
+ ? (this.openMobile = !this.openMobile)
+ : this.setOpen(!this.open);
+ };
+}
+
+const SYMBOL_KEY = "scn-sidebar";
+
+/**
+ * Instantiates a new `SidebarState` instance and sets it in the context.
+ *
+ * @param props The constructor props for the `SidebarState` class.
+ * @returns The `SidebarState` instance.
+ */
+export function setSidebar(props: SidebarStateProps): SidebarState {
+ return setContext(Symbol.for(SYMBOL_KEY), new SidebarState(props));
+}
+
+/**
+ * Retrieves the `SidebarState` instance from the context. This is a class instance,
+ * so you cannot destructure it.
+ * @returns The `SidebarState` instance.
+ */
+export function useSidebar(): SidebarState {
+ return getContext(Symbol.for(SYMBOL_KEY));
+}
diff --git a/ui-svelte/src/lib/components/ui/sidebar/index.ts b/ui-svelte/src/lib/components/ui/sidebar/index.ts
new file mode 100644
index 00000000..318a3417
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/index.ts
@@ -0,0 +1,75 @@
+import { useSidebar } from "./context.svelte.js";
+import Content from "./sidebar-content.svelte";
+import Footer from "./sidebar-footer.svelte";
+import GroupAction from "./sidebar-group-action.svelte";
+import GroupContent from "./sidebar-group-content.svelte";
+import GroupLabel from "./sidebar-group-label.svelte";
+import Group from "./sidebar-group.svelte";
+import Header from "./sidebar-header.svelte";
+import Input from "./sidebar-input.svelte";
+import Inset from "./sidebar-inset.svelte";
+import MenuAction from "./sidebar-menu-action.svelte";
+import MenuBadge from "./sidebar-menu-badge.svelte";
+import MenuButton from "./sidebar-menu-button.svelte";
+import MenuItem from "./sidebar-menu-item.svelte";
+import MenuSkeleton from "./sidebar-menu-skeleton.svelte";
+import MenuSubButton from "./sidebar-menu-sub-button.svelte";
+import MenuSubItem from "./sidebar-menu-sub-item.svelte";
+import MenuSub from "./sidebar-menu-sub.svelte";
+import Menu from "./sidebar-menu.svelte";
+import Provider from "./sidebar-provider.svelte";
+import Rail from "./sidebar-rail.svelte";
+import Separator from "./sidebar-separator.svelte";
+import Trigger from "./sidebar-trigger.svelte";
+import Root from "./sidebar.svelte";
+
+export {
+ Content,
+ Footer,
+ Group,
+ GroupAction,
+ GroupContent,
+ GroupLabel,
+ Header,
+ Input,
+ Inset,
+ Menu,
+ MenuAction,
+ MenuBadge,
+ MenuButton,
+ MenuItem,
+ MenuSkeleton,
+ MenuSub,
+ MenuSubButton,
+ MenuSubItem,
+ Provider,
+ Rail,
+ Root,
+ Separator,
+ //
+ Root as Sidebar,
+ Content as SidebarContent,
+ Footer as SidebarFooter,
+ Group as SidebarGroup,
+ GroupAction as SidebarGroupAction,
+ GroupContent as SidebarGroupContent,
+ GroupLabel as SidebarGroupLabel,
+ Header as SidebarHeader,
+ Input as SidebarInput,
+ Inset as SidebarInset,
+ Menu as SidebarMenu,
+ MenuAction as SidebarMenuAction,
+ MenuBadge as SidebarMenuBadge,
+ MenuButton as SidebarMenuButton,
+ MenuItem as SidebarMenuItem,
+ MenuSkeleton as SidebarMenuSkeleton,
+ MenuSub as SidebarMenuSub,
+ MenuSubButton as SidebarMenuSubButton,
+ MenuSubItem as SidebarMenuSubItem,
+ Provider as SidebarProvider,
+ Rail as SidebarRail,
+ Separator as SidebarSeparator,
+ Trigger as SidebarTrigger,
+ Trigger,
+ useSidebar,
+};
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-content.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-content.svelte
new file mode 100644
index 00000000..2a8d7ea0
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-content.svelte
@@ -0,0 +1,24 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-footer.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-footer.svelte
new file mode 100644
index 00000000..496c6c0b
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-footer.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-group-action.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-group-action.svelte
new file mode 100644
index 00000000..1d07b978
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-group-action.svelte
@@ -0,0 +1,33 @@
+
+
+{#if child}
+ {@render child({ props: mergedProps })}
+{:else}
+
+{/if}
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-group-content.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-group-content.svelte
new file mode 100644
index 00000000..7835b0e3
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-group-content.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-group-label.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-group-label.svelte
new file mode 100644
index 00000000..518216c2
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-group-label.svelte
@@ -0,0 +1,33 @@
+
+
+{#if child}
+ {@render child({ props: mergedProps })}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-group.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-group.svelte
new file mode 100644
index 00000000..b5880d7c
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-group.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-header.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-header.svelte
new file mode 100644
index 00000000..b54754ac
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-header.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-input.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-input.svelte
new file mode 100644
index 00000000..19b36660
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-input.svelte
@@ -0,0 +1,21 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-inset.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-inset.svelte
new file mode 100644
index 00000000..19a787cb
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-inset.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-action.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-action.svelte
new file mode 100644
index 00000000..26c81ed6
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-action.svelte
@@ -0,0 +1,37 @@
+
+
+{#if child}
+ {@render child({ props: mergedProps })}
+{:else}
+
+{/if}
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte
new file mode 100644
index 00000000..6cecdb41
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte
@@ -0,0 +1,24 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-button.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-button.svelte
new file mode 100644
index 00000000..e0c9c25e
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-button.svelte
@@ -0,0 +1,102 @@
+
+
+
+
+{#snippet Button({ props }: { props?: Record })}
+ {@const mergedProps = mergeProps(buttonProps, props)}
+ {#if child}
+ {@render child({ props: mergedProps })}
+ {:else}
+
+ {/if}
+{/snippet}
+
+{#if !tooltipContent}
+ {@render Button({})}
+{:else}
+
+
+ {#snippet child({ props })}
+ {@render Button({ props })}
+ {/snippet}
+
+
+ {#if typeof tooltipContent === "string"}
+ {tooltipContent}
+ {:else if tooltipContent}
+ {@render tooltipContent()}
+ {/if}
+
+
+{/if}
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-item.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-item.svelte
new file mode 100644
index 00000000..4db44532
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-item.svelte
@@ -0,0 +1,21 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte
new file mode 100644
index 00000000..1e1249bf
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte
@@ -0,0 +1,36 @@
+
+
+
+ {#if showIcon}
+
+ {/if}
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte
new file mode 100644
index 00000000..09ff228f
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte
@@ -0,0 +1,39 @@
+
+
+{#if child}
+ {@render child({ props: mergedProps })}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte
new file mode 100644
index 00000000..681d0f1d
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte
@@ -0,0 +1,21 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte
new file mode 100644
index 00000000..29614ee1
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte
@@ -0,0 +1,21 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu.svelte
new file mode 100644
index 00000000..a8cdefd7
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-menu.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-provider.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-provider.svelte
new file mode 100644
index 00000000..5b0d0aa2
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-provider.svelte
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+ {@render children?.()}
+
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-rail.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-rail.svelte
new file mode 100644
index 00000000..f65c8698
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-rail.svelte
@@ -0,0 +1,36 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-separator.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-separator.svelte
new file mode 100644
index 00000000..18d791bd
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-separator.svelte
@@ -0,0 +1,19 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar-trigger.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar-trigger.svelte
new file mode 100644
index 00000000..dd2dc503
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar-trigger.svelte
@@ -0,0 +1,36 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/sidebar/sidebar.svelte b/ui-svelte/src/lib/components/ui/sidebar/sidebar.svelte
new file mode 100644
index 00000000..9c90d4f8
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/sidebar/sidebar.svelte
@@ -0,0 +1,108 @@
+
+
+{#if collapsible === "none"}
+
+ {@render children?.()}
+
+{:else if sidebar.isMobile}
+ sidebar.openMobile, (v) => sidebar.setOpenMobile(v)}
+ {...restProps}
+ >
+ button]:hidden",
+ className
+ )}
+ style="--sidebar-width: {SIDEBAR_WIDTH_MOBILE};"
+ {side}
+ >
+
+ Sidebar
+ Displays the mobile sidebar.
+
+
+ {@render children?.()}
+
+
+
+{:else}
+
+{/if}
diff --git a/ui-svelte/src/lib/components/ui/skeleton/index.ts b/ui-svelte/src/lib/components/ui/skeleton/index.ts
new file mode 100644
index 00000000..186db219
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/skeleton/index.ts
@@ -0,0 +1,7 @@
+import Root from "./skeleton.svelte";
+
+export {
+ Root,
+ //
+ Root as Skeleton,
+};
diff --git a/ui-svelte/src/lib/components/ui/skeleton/skeleton.svelte b/ui-svelte/src/lib/components/ui/skeleton/skeleton.svelte
new file mode 100644
index 00000000..1a940eee
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/skeleton/skeleton.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/switch/index.ts b/ui-svelte/src/lib/components/ui/switch/index.ts
new file mode 100644
index 00000000..f5533db7
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/switch/index.ts
@@ -0,0 +1,7 @@
+import Root from "./switch.svelte";
+
+export {
+ Root,
+ //
+ Root as Switch,
+};
diff --git a/ui-svelte/src/lib/components/ui/switch/switch.svelte b/ui-svelte/src/lib/components/ui/switch/switch.svelte
new file mode 100644
index 00000000..6aa57d5b
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/switch/switch.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/table/index.ts b/ui-svelte/src/lib/components/ui/table/index.ts
new file mode 100644
index 00000000..14695c81
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/table/index.ts
@@ -0,0 +1,28 @@
+import Root from "./table.svelte";
+import Body from "./table-body.svelte";
+import Caption from "./table-caption.svelte";
+import Cell from "./table-cell.svelte";
+import Footer from "./table-footer.svelte";
+import Head from "./table-head.svelte";
+import Header from "./table-header.svelte";
+import Row from "./table-row.svelte";
+
+export {
+ Root,
+ Body,
+ Caption,
+ Cell,
+ Footer,
+ Head,
+ Header,
+ Row,
+ //
+ Root as Table,
+ Body as TableBody,
+ Caption as TableCaption,
+ Cell as TableCell,
+ Footer as TableFooter,
+ Head as TableHead,
+ Header as TableHeader,
+ Row as TableRow,
+};
diff --git a/ui-svelte/src/lib/components/ui/table/table-body.svelte b/ui-svelte/src/lib/components/ui/table/table-body.svelte
new file mode 100644
index 00000000..935feae7
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/table/table-body.svelte
@@ -0,0 +1,15 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/table/table-caption.svelte b/ui-svelte/src/lib/components/ui/table/table-caption.svelte
new file mode 100644
index 00000000..4696cff5
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/table/table-caption.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/table/table-cell.svelte b/ui-svelte/src/lib/components/ui/table/table-cell.svelte
new file mode 100644
index 00000000..a998bf68
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/table/table-cell.svelte
@@ -0,0 +1,15 @@
+
+
+
+ {@render children?.()}
+ |
diff --git a/ui-svelte/src/lib/components/ui/table/table-footer.svelte b/ui-svelte/src/lib/components/ui/table/table-footer.svelte
new file mode 100644
index 00000000..b9b14ebf
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/table/table-footer.svelte
@@ -0,0 +1,20 @@
+
+
+tr]:last:border-b-0", className)}
+ {...restProps}
+>
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/table/table-head.svelte b/ui-svelte/src/lib/components/ui/table/table-head.svelte
new file mode 100644
index 00000000..267c4e08
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/table/table-head.svelte
@@ -0,0 +1,15 @@
+
+
+
+ {@render children?.()}
+ |
diff --git a/ui-svelte/src/lib/components/ui/table/table-header.svelte b/ui-svelte/src/lib/components/ui/table/table-header.svelte
new file mode 100644
index 00000000..f47d2597
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/table/table-header.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/table/table-row.svelte b/ui-svelte/src/lib/components/ui/table/table-row.svelte
new file mode 100644
index 00000000..90b4e2a2
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/table/table-row.svelte
@@ -0,0 +1,15 @@
+
+
+
+ {@render children?.()}
+
diff --git a/ui-svelte/src/lib/components/ui/table/table.svelte b/ui-svelte/src/lib/components/ui/table/table.svelte
new file mode 100644
index 00000000..d95a02ea
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/table/table.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+ {@render children?.()}
+
+
diff --git a/ui-svelte/src/lib/components/ui/tabs/index.ts b/ui-svelte/src/lib/components/ui/tabs/index.ts
new file mode 100644
index 00000000..31267e58
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/tabs/index.ts
@@ -0,0 +1,18 @@
+import Root from "./tabs.svelte";
+import Content from "./tabs-content.svelte";
+import List, { tabsListVariants, type TabsListVariant } from "./tabs-list.svelte";
+import Trigger from "./tabs-trigger.svelte";
+
+export {
+ Root,
+ Content,
+ List,
+ Trigger,
+ tabsListVariants,
+ type TabsListVariant,
+ //
+ Root as Tabs,
+ Content as TabsContent,
+ List as TabsList,
+ Trigger as TabsTrigger,
+};
diff --git a/ui-svelte/src/lib/components/ui/tabs/tabs-content.svelte b/ui-svelte/src/lib/components/ui/tabs/tabs-content.svelte
new file mode 100644
index 00000000..394ab3e8
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/tabs/tabs-content.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/tabs/tabs-list.svelte b/ui-svelte/src/lib/components/ui/tabs/tabs-list.svelte
new file mode 100644
index 00000000..18dce00a
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/tabs/tabs-list.svelte
@@ -0,0 +1,40 @@
+
+
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/tabs/tabs-trigger.svelte b/ui-svelte/src/lib/components/ui/tabs/tabs-trigger.svelte
new file mode 100644
index 00000000..8a0be4ae
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/tabs/tabs-trigger.svelte
@@ -0,0 +1,23 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/tabs/tabs.svelte b/ui-svelte/src/lib/components/ui/tabs/tabs.svelte
new file mode 100644
index 00000000..bfb900db
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/tabs/tabs.svelte
@@ -0,0 +1,19 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/textarea/index.ts b/ui-svelte/src/lib/components/ui/textarea/index.ts
new file mode 100644
index 00000000..ace797a8
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/textarea/index.ts
@@ -0,0 +1,7 @@
+import Root from "./textarea.svelte";
+
+export {
+ Root,
+ //
+ Root as Textarea,
+};
diff --git a/ui-svelte/src/lib/components/ui/textarea/textarea.svelte b/ui-svelte/src/lib/components/ui/textarea/textarea.svelte
new file mode 100644
index 00000000..2e779ff6
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/textarea/textarea.svelte
@@ -0,0 +1,23 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/toggle-group/index.ts b/ui-svelte/src/lib/components/ui/toggle-group/index.ts
new file mode 100644
index 00000000..12b14b96
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/components/ui/toggle-group/toggle-group-item.svelte b/ui-svelte/src/lib/components/ui/toggle-group/toggle-group-item.svelte
new file mode 100644
index 00000000..f685ae31
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/toggle-group/toggle-group-item.svelte
@@ -0,0 +1,35 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/toggle-group/toggle-group.svelte b/ui-svelte/src/lib/components/ui/toggle-group/toggle-group.svelte
new file mode 100644
index 00000000..98b062bb
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/toggle-group/toggle-group.svelte
@@ -0,0 +1,75 @@
+
+
+
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/toggle/index.ts b/ui-svelte/src/lib/components/ui/toggle/index.ts
new file mode 100644
index 00000000..8cb2936f
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/components/ui/toggle/toggle.svelte b/ui-svelte/src/lib/components/ui/toggle/toggle.svelte
new file mode 100644
index 00000000..8e50317e
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/toggle/toggle.svelte
@@ -0,0 +1,51 @@
+
+
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/tooltip/index.ts b/ui-svelte/src/lib/components/ui/tooltip/index.ts
new file mode 100644
index 00000000..17186042
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/components/ui/tooltip/tooltip-content.svelte b/ui-svelte/src/lib/components/ui/tooltip/tooltip-content.svelte
new file mode 100644
index 00000000..0cf06949
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/tooltip/tooltip-content.svelte
@@ -0,0 +1,52 @@
+
+
+
+
+ {@render children?.()}
+
+ {#snippet child({ props })}
+
+ {/snippet}
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/tooltip/tooltip-portal.svelte b/ui-svelte/src/lib/components/ui/tooltip/tooltip-portal.svelte
new file mode 100644
index 00000000..d234f7d7
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/tooltip/tooltip-portal.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/tooltip/tooltip-provider.svelte b/ui-svelte/src/lib/components/ui/tooltip/tooltip-provider.svelte
new file mode 100644
index 00000000..6dba9a67
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/tooltip/tooltip-provider.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/tooltip/tooltip-trigger.svelte b/ui-svelte/src/lib/components/ui/tooltip/tooltip-trigger.svelte
new file mode 100644
index 00000000..c4b21fc7
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/tooltip/tooltip-trigger.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/components/ui/tooltip/tooltip.svelte b/ui-svelte/src/lib/components/ui/tooltip/tooltip.svelte
new file mode 100644
index 00000000..03c9a3d9
--- /dev/null
+++ b/ui-svelte/src/lib/components/ui/tooltip/tooltip.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ui-svelte/src/lib/hooks/is-mobile.svelte.ts b/ui-svelte/src/lib/hooks/is-mobile.svelte.ts
new file mode 100644
index 00000000..4829c00b
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/src/lib/utils.ts b/ui-svelte/src/lib/utils.ts
new file mode 100644
index 00000000..55b3a918
--- /dev/null
+++ b/ui-svelte/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/ui-svelte/tsconfig.json b/ui-svelte/tsconfig.json
index 80fb577b..0d590bbf 100644
--- a/ui-svelte/tsconfig.json
+++ b/ui-svelte/tsconfig.json
@@ -14,7 +14,12 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
- "verbatimModuleSyntax": true
+ "verbatimModuleSyntax": true,
+ "baseUrl": ".",
+ "paths": {
+ "$lib": ["./src/lib"],
+ "$lib/*": ["./src/lib/*"]
+ }
},
"include": ["src/**/*.ts", "src/**/*.svelte"]
}
diff --git a/ui-svelte/vite.config.ts b/ui-svelte/vite.config.ts
index e2ac6b36..adc03eea 100644
--- a/ui-svelte/vite.config.ts
+++ b/ui-svelte/vite.config.ts
@@ -2,6 +2,7 @@ import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import tailwindcss from "@tailwindcss/vite";
import { compression } from "vite-plugin-compression2";
+import path from "node:path";
// https://vite.dev/config/
export default defineConfig({
@@ -21,6 +22,11 @@ export default defineConfig({
}),
],
base: "/ui/",
+ resolve: {
+ alias: {
+ $lib: path.resolve(__dirname, "./src/lib"),
+ },
+ },
build: {
outDir: "../internal/server/ui_dist",
assetsDir: "assets",