account screen
This commit is contained in:
parent
4233f4f17d
commit
61ba8c1b9f
27 changed files with 628 additions and 11 deletions
15
jsrepo.json
Normal file
15
jsrepo.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://unpkg.com/jsrepo@2.3.1/schemas/project-config.json",
|
||||||
|
"repos": ["@ieedan/shadcn-svelte-extras"],
|
||||||
|
"includeTests": false,
|
||||||
|
"watermark": true,
|
||||||
|
"formatter": "prettier",
|
||||||
|
"configFiles": {},
|
||||||
|
"paths": {
|
||||||
|
"*": "$lib/blocks",
|
||||||
|
"utils": "$lib/utils",
|
||||||
|
"ui": "$lib/components/ui",
|
||||||
|
"actions": "$lib/actions",
|
||||||
|
"hooks": "$lib/hooks"
|
||||||
|
}
|
||||||
|
}
|
||||||
17
package.json
17
package.json
|
|
@ -4,7 +4,7 @@
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently \"convex dev\" \"vite dev\"",
|
"dev": "concurrently -n \"convex,vite\" -c \"blue.bold,green.bold\" \"convex dev\" \"vite dev\"",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"prepare": "svelte-kit sync || echo ''",
|
"prepare": "svelte-kit sync || echo ''",
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.2.5",
|
"@eslint/compat": "^1.2.5",
|
||||||
"@eslint/js": "^9.18.0",
|
"@eslint/js": "^9.18.0",
|
||||||
|
"@lucide/svelte": "^0.515.0",
|
||||||
"@playwright/test": "^1.49.1",
|
"@playwright/test": "^1.49.1",
|
||||||
"@sveltejs/adapter-auto": "^6.0.0",
|
"@sveltejs/adapter-auto": "^6.0.0",
|
||||||
"@sveltejs/kit": "^2.16.0",
|
"@sveltejs/kit": "^2.16.0",
|
||||||
|
|
@ -26,11 +27,17 @@
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/svelte": "^5.2.4",
|
"@testing-library/svelte": "^5.2.4",
|
||||||
|
"bits-ui": "^2.6.2",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"concurrently": "^9.1.2",
|
||||||
|
"convex": "^1.24.8",
|
||||||
|
"convex-svelte": "^0.0.11",
|
||||||
"eslint": "^9.18.0",
|
"eslint": "^9.18.0",
|
||||||
"eslint-config-prettier": "^10.0.1",
|
"eslint-config-prettier": "^10.0.1",
|
||||||
"eslint-plugin-svelte": "^3.0.0",
|
"eslint-plugin-svelte": "^3.0.0",
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.0.0",
|
||||||
|
"melt": "^0.35.0",
|
||||||
"mode-watcher": "^1.0.8",
|
"mode-watcher": "^1.0.8",
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.4.2",
|
||||||
"prettier-plugin-svelte": "^3.3.3",
|
"prettier-plugin-svelte": "^3.3.3",
|
||||||
|
|
@ -38,15 +45,13 @@
|
||||||
"runed": "^0.28.0",
|
"runed": "^0.28.0",
|
||||||
"svelte": "^5.0.0",
|
"svelte": "^5.0.0",
|
||||||
"svelte-check": "^4.0.0",
|
"svelte-check": "^4.0.0",
|
||||||
|
"tailwind-merge": "^3.3.1",
|
||||||
|
"tailwind-variants": "^1.0.0",
|
||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.0.0",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"typescript-eslint": "^8.20.0",
|
"typescript-eslint": "^8.20.0",
|
||||||
"vite": "^6.2.6",
|
"vite": "^6.2.6",
|
||||||
"vitest": "^3.2.3",
|
"vitest": "^3.2.3"
|
||||||
"concurrently": "^9.1.2",
|
|
||||||
"convex": "^1.24.8",
|
|
||||||
"convex-svelte": "^0.0.11",
|
|
||||||
"melt": "^0.35.0"
|
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
|
|
|
||||||
96
pnpm-lock.yaml
generated
96
pnpm-lock.yaml
generated
|
|
@ -14,6 +14,9 @@ importers:
|
||||||
'@eslint/js':
|
'@eslint/js':
|
||||||
specifier: ^9.18.0
|
specifier: ^9.18.0
|
||||||
version: 9.28.0
|
version: 9.28.0
|
||||||
|
'@lucide/svelte':
|
||||||
|
specifier: ^0.515.0
|
||||||
|
version: 0.515.0(svelte@5.34.1)
|
||||||
'@playwright/test':
|
'@playwright/test':
|
||||||
specifier: ^1.49.1
|
specifier: ^1.49.1
|
||||||
version: 1.53.0
|
version: 1.53.0
|
||||||
|
|
@ -35,6 +38,12 @@ importers:
|
||||||
'@testing-library/svelte':
|
'@testing-library/svelte':
|
||||||
specifier: ^5.2.4
|
specifier: ^5.2.4
|
||||||
version: 5.2.8(svelte@5.34.1)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(lightningcss@1.30.1))(vitest@3.2.3(@types/node@24.0.1)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1))
|
version: 5.2.8(svelte@5.34.1)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(lightningcss@1.30.1))(vitest@3.2.3(@types/node@24.0.1)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1))
|
||||||
|
bits-ui:
|
||||||
|
specifier: ^2.6.2
|
||||||
|
version: 2.6.2(@internationalized/date@3.8.2)(svelte@5.34.1)
|
||||||
|
clsx:
|
||||||
|
specifier: ^2.1.1
|
||||||
|
version: 2.1.1
|
||||||
concurrently:
|
concurrently:
|
||||||
specifier: ^9.1.2
|
specifier: ^9.1.2
|
||||||
version: 9.1.2
|
version: 9.1.2
|
||||||
|
|
@ -83,6 +92,12 @@ importers:
|
||||||
svelte-check:
|
svelte-check:
|
||||||
specifier: ^4.0.0
|
specifier: ^4.0.0
|
||||||
version: 4.2.1(picomatch@4.0.2)(svelte@5.34.1)(typescript@5.8.3)
|
version: 4.2.1(picomatch@4.0.2)(svelte@5.34.1)(typescript@5.8.3)
|
||||||
|
tailwind-merge:
|
||||||
|
specifier: ^3.3.1
|
||||||
|
version: 3.3.1
|
||||||
|
tailwind-variants:
|
||||||
|
specifier: ^1.0.0
|
||||||
|
version: 1.0.0(tailwindcss@4.1.10)
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^4.0.0
|
specifier: ^4.0.0
|
||||||
version: 4.1.10
|
version: 4.1.10
|
||||||
|
|
@ -531,6 +546,9 @@ packages:
|
||||||
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
||||||
engines: {node: '>=18.18'}
|
engines: {node: '>=18.18'}
|
||||||
|
|
||||||
|
'@internationalized/date@3.8.2':
|
||||||
|
resolution: {integrity: sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==}
|
||||||
|
|
||||||
'@isaacs/fs-minipass@4.0.1':
|
'@isaacs/fs-minipass@4.0.1':
|
||||||
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
|
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
@ -557,6 +575,11 @@ packages:
|
||||||
'@jridgewell/trace-mapping@0.3.25':
|
'@jridgewell/trace-mapping@0.3.25':
|
||||||
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
||||||
|
|
||||||
|
'@lucide/svelte@0.515.0':
|
||||||
|
resolution: {integrity: sha512-CEAyqcZmNBfYzVgaRmK2RFJP5tnbXxekRyDk0XX/eZQRfsJmkDvmQwXNX8C869BgNeryzmrRyjHhUL6g9ZOHNA==}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^5
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
@ -714,6 +737,9 @@ packages:
|
||||||
svelte: ^5.0.0
|
svelte: ^5.0.0
|
||||||
vite: ^6.0.0
|
vite: ^6.0.0
|
||||||
|
|
||||||
|
'@swc/helpers@0.5.17':
|
||||||
|
resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==}
|
||||||
|
|
||||||
'@tailwindcss/node@4.1.10':
|
'@tailwindcss/node@4.1.10':
|
||||||
resolution: {integrity: sha512-2ACf1znY5fpRBwRhMgj9ZXvb2XZW8qs+oTfotJ2C5xR0/WNL7UHZ7zXl6s+rUqedL1mNi+0O+WQr5awGowS3PQ==}
|
resolution: {integrity: sha512-2ACf1znY5fpRBwRhMgj9ZXvb2XZW8qs+oTfotJ2C5xR0/WNL7UHZ7zXl6s+rUqedL1mNi+0O+WQr5awGowS3PQ==}
|
||||||
|
|
||||||
|
|
@ -991,6 +1017,13 @@ packages:
|
||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
|
bits-ui@2.6.2:
|
||||||
|
resolution: {integrity: sha512-OlPSUAT+ENhtRarPjABljca1cCljyoAqOZKfgjCB8PxQii2fL0AKnzObhnEdhZKwYdpXczEtNOYqUUNYwliaWA==}
|
||||||
|
engines: {node: '>=20', pnpm: '>=8.7.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@internationalized/date': ^3.8.1
|
||||||
|
svelte: ^5.33.0
|
||||||
|
|
||||||
brace-expansion@1.1.12:
|
brace-expansion@1.1.12:
|
||||||
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
||||||
|
|
||||||
|
|
@ -1954,6 +1987,12 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^5.0.0
|
svelte: ^5.0.0
|
||||||
|
|
||||||
|
svelte-toolbelt@0.9.1:
|
||||||
|
resolution: {integrity: sha512-wBX6MtYw/kpht80j5zLpxJyR9soLizXPIAIWEVd9llAi17SR44ZdG291bldjB7r/K5duC0opDFcuhk2cA1hb8g==}
|
||||||
|
engines: {node: '>=18', pnpm: '>=8.7.0'}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^5.30.2
|
||||||
|
|
||||||
svelte@5.34.1:
|
svelte@5.34.1:
|
||||||
resolution: {integrity: sha512-jWNnN2hZFNtnzKPptCcJHBWrD9CtbHPDwIRIODufOYaWkR0kLmAIlM384lMt4ucwuIRX4hCJwD2D8ZtEcGJQ0Q==}
|
resolution: {integrity: sha512-jWNnN2hZFNtnzKPptCcJHBWrD9CtbHPDwIRIODufOYaWkR0kLmAIlM384lMt4ucwuIRX4hCJwD2D8ZtEcGJQ0Q==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
@ -1961,6 +2000,21 @@ packages:
|
||||||
symbol-tree@3.2.4:
|
symbol-tree@3.2.4:
|
||||||
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
||||||
|
|
||||||
|
tabbable@6.2.0:
|
||||||
|
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
|
||||||
|
|
||||||
|
tailwind-merge@3.0.2:
|
||||||
|
resolution: {integrity: sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==}
|
||||||
|
|
||||||
|
tailwind-merge@3.3.1:
|
||||||
|
resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==}
|
||||||
|
|
||||||
|
tailwind-variants@1.0.0:
|
||||||
|
resolution: {integrity: sha512-2WSbv4ulEEyuBKomOunut65D8UZwxrHoRfYnxGcQNnHqlSCp2+B7Yz2W+yrNDrxRodOXtGD/1oCcKGNBnUqMqA==}
|
||||||
|
engines: {node: '>=16.x', pnpm: '>=7.x'}
|
||||||
|
peerDependencies:
|
||||||
|
tailwindcss: '*'
|
||||||
|
|
||||||
tailwindcss@4.1.10:
|
tailwindcss@4.1.10:
|
||||||
resolution: {integrity: sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==}
|
resolution: {integrity: sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==}
|
||||||
|
|
||||||
|
|
@ -2493,6 +2547,10 @@ snapshots:
|
||||||
|
|
||||||
'@humanwhocodes/retry@0.4.3': {}
|
'@humanwhocodes/retry@0.4.3': {}
|
||||||
|
|
||||||
|
'@internationalized/date@3.8.2':
|
||||||
|
dependencies:
|
||||||
|
'@swc/helpers': 0.5.17
|
||||||
|
|
||||||
'@isaacs/fs-minipass@4.0.1':
|
'@isaacs/fs-minipass@4.0.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
minipass: 7.1.2
|
minipass: 7.1.2
|
||||||
|
|
@ -2518,6 +2576,10 @@ snapshots:
|
||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.0
|
'@jridgewell/sourcemap-codec': 1.5.0
|
||||||
|
|
||||||
|
'@lucide/svelte@0.515.0(svelte@5.34.1)':
|
||||||
|
dependencies:
|
||||||
|
svelte: 5.34.1
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nodelib/fs.stat': 2.0.5
|
'@nodelib/fs.stat': 2.0.5
|
||||||
|
|
@ -2647,6 +2709,10 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
'@swc/helpers@0.5.17':
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@tailwindcss/node@4.1.10':
|
'@tailwindcss/node@4.1.10':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
|
|
@ -2941,6 +3007,18 @@ snapshots:
|
||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
|
bits-ui@2.6.2(@internationalized/date@3.8.2)(svelte@5.34.1):
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/core': 1.7.1
|
||||||
|
'@floating-ui/dom': 1.7.1
|
||||||
|
'@internationalized/date': 3.8.2
|
||||||
|
css.escape: 1.5.1
|
||||||
|
esm-env: 1.2.2
|
||||||
|
runed: 0.28.0(svelte@5.34.1)
|
||||||
|
svelte: 5.34.1
|
||||||
|
svelte-toolbelt: 0.9.1(svelte@5.34.1)
|
||||||
|
tabbable: 6.2.0
|
||||||
|
|
||||||
brace-expansion@1.1.12:
|
brace-expansion@1.1.12:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
|
|
@ -3836,6 +3914,13 @@ snapshots:
|
||||||
style-to-object: 1.0.9
|
style-to-object: 1.0.9
|
||||||
svelte: 5.34.1
|
svelte: 5.34.1
|
||||||
|
|
||||||
|
svelte-toolbelt@0.9.1(svelte@5.34.1):
|
||||||
|
dependencies:
|
||||||
|
clsx: 2.1.1
|
||||||
|
runed: 0.28.0(svelte@5.34.1)
|
||||||
|
style-to-object: 1.0.9
|
||||||
|
svelte: 5.34.1
|
||||||
|
|
||||||
svelte@5.34.1:
|
svelte@5.34.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
|
|
@ -3855,6 +3940,17 @@ snapshots:
|
||||||
|
|
||||||
symbol-tree@3.2.4: {}
|
symbol-tree@3.2.4: {}
|
||||||
|
|
||||||
|
tabbable@6.2.0: {}
|
||||||
|
|
||||||
|
tailwind-merge@3.0.2: {}
|
||||||
|
|
||||||
|
tailwind-merge@3.3.1: {}
|
||||||
|
|
||||||
|
tailwind-variants@1.0.0(tailwindcss@4.1.10):
|
||||||
|
dependencies:
|
||||||
|
tailwind-merge: 3.0.2
|
||||||
|
tailwindcss: 4.1.10
|
||||||
|
|
||||||
tailwindcss@4.1.10: {}
|
tailwindcss@4.1.10: {}
|
||||||
|
|
||||||
tapable@2.2.2: {}
|
tapable@2.2.2: {}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
@import 'tailwindcss';
|
@import 'tailwindcss';
|
||||||
|
|
||||||
|
@custom-variant dark (&:where(.dark, .dark *));
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: oklch(0.9754 0.0084 325.6414);
|
--background: oklch(0.9754 0.0084 325.6414);
|
||||||
--foreground: oklch(0.3257 0.1161 325.0372);
|
--foreground: oklch(0.3257 0.1161 325.0372);
|
||||||
|
|
@ -153,4 +155,4 @@
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
110
src/lib/actions/active.svelte.ts
Normal file
110
src/lib/actions/active.svelte.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
Installed from @ieedan/shadcn-svelte-extras
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { page } from '$app/state';
|
||||||
|
import { untrack } from 'svelte';
|
||||||
|
import { createAttachmentKey } from 'svelte/attachments';
|
||||||
|
|
||||||
|
export type Options = {
|
||||||
|
/** Determines if the route should be active for subdirectories.
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
activeForSubdirectories?: boolean;
|
||||||
|
/** Determines if the href of the `<a/>` tag is a `#` route
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
isHash?: boolean;
|
||||||
|
/** Determines if the href of the `<a/>` tag is a search route
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
isSearch?: boolean;
|
||||||
|
url: URL;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Sets the `data-active` attribute on an `<a/>` tag based on its 'active' state.
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
* @param opts
|
||||||
|
*
|
||||||
|
* ## Usage
|
||||||
|
* ```svelte
|
||||||
|
* <a href="/" use:active>Route</a>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function active(node: HTMLAnchorElement, opts: Omit<Options, 'url'> = {}) {
|
||||||
|
checkIsActive(node.href, { ...opts, url: page.url }).toString();
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
|
page.url;
|
||||||
|
|
||||||
|
untrack(() => {
|
||||||
|
node.setAttribute(
|
||||||
|
'data-active',
|
||||||
|
checkIsActive(node.href, { ...opts, url: page.url }).toString()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the `data-active` attribute on an `<a/>` tag based on its 'active' state.
|
||||||
|
*
|
||||||
|
* @param opts
|
||||||
|
* @returns
|
||||||
|
*
|
||||||
|
* ## Usage
|
||||||
|
* ```svelte
|
||||||
|
* <a href="/" {...attachActive()}>Route</a>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function attachActive(opts: Omit<Options, 'url'> = {}) {
|
||||||
|
return {
|
||||||
|
[createAttachmentKey()]: (node: HTMLAnchorElement) => active(node, opts)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const checkIsActive = (
|
||||||
|
nodeHref: string,
|
||||||
|
{ activeForSubdirectories, url, isHash, isSearch }: Options
|
||||||
|
): boolean => {
|
||||||
|
let href: string = new URL(nodeHref).pathname;
|
||||||
|
|
||||||
|
if (isHash) {
|
||||||
|
href = new URL(nodeHref).hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchParamName: string | undefined = undefined;
|
||||||
|
let searchParamValue: string | undefined = undefined;
|
||||||
|
|
||||||
|
if (isSearch) {
|
||||||
|
const tempUrl = new URL(nodeHref);
|
||||||
|
|
||||||
|
for (const [key, value] of tempUrl.searchParams.entries()) {
|
||||||
|
searchParamName = key;
|
||||||
|
searchParamValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
href = new URL(nodeHref).search;
|
||||||
|
}
|
||||||
|
|
||||||
|
const samePath = href === url.pathname;
|
||||||
|
|
||||||
|
const isParentRoute: boolean =
|
||||||
|
(activeForSubdirectories == undefined || activeForSubdirectories) &&
|
||||||
|
url.pathname.startsWith(href ?? '');
|
||||||
|
|
||||||
|
const isHashRoute: boolean =
|
||||||
|
isHash == true && (url.hash == href || ((href == '#' || href == '#/') && url.hash == ''));
|
||||||
|
|
||||||
|
const isSearchRoute: boolean =
|
||||||
|
isSearch === true &&
|
||||||
|
searchParamName !== undefined &&
|
||||||
|
searchParamValue !== undefined &&
|
||||||
|
(url.searchParams.get(searchParamName) ?? '/') === searchParamValue;
|
||||||
|
|
||||||
|
return samePath || isParentRoute || isHashRoute || isSearchRoute;
|
||||||
|
};
|
||||||
|
|
@ -5,4 +5,4 @@ export default defineSchema({
|
||||||
user_keys: defineTable({
|
user_keys: defineTable({
|
||||||
openRouter: v.string()
|
openRouter: v.string()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
||||||
128
src/lib/components/ui/button/button.svelte
Normal file
128
src/lib/components/ui/button/button.svelte
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
<!--
|
||||||
|
Installed from @ieedan/shadcn-svelte-extras
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts" module>
|
||||||
|
import type { WithChildren, WithoutChildren } from 'bits-ui';
|
||||||
|
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
||||||
|
import { type VariantProps, tv } from 'tailwind-variants';
|
||||||
|
|
||||||
|
export const buttonVariants = tv({
|
||||||
|
base: "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive focus-visible:border-ring focus-visible:ring-ring/50 relative inline-flex shrink-0 items-center justify-center gap-2 overflow-hidden rounded-md text-sm font-medium whitespace-nowrap outline-hidden transition-all select-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-2xs',
|
||||||
|
destructive:
|
||||||
|
'bg-destructive hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 text-white shadow-2xs',
|
||||||
|
outline:
|
||||||
|
'bg-background hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 border shadow-2xs',
|
||||||
|
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-2xs',
|
||||||
|
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
||||||
|
link: 'text-primary underline-offset-4 hover:underline'
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
||||||
|
sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
|
||||||
|
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
|
||||||
|
icon: 'size-9'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'default',
|
||||||
|
size: 'default'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
|
||||||
|
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
|
||||||
|
|
||||||
|
export type ButtonPropsWithoutHTML = WithChildren<{
|
||||||
|
ref?: HTMLElement | null;
|
||||||
|
variant?: ButtonVariant;
|
||||||
|
size?: ButtonSize;
|
||||||
|
loading?: boolean;
|
||||||
|
onClickPromise?: (
|
||||||
|
e: MouseEvent & {
|
||||||
|
currentTarget: EventTarget & HTMLButtonElement;
|
||||||
|
}
|
||||||
|
) => Promise<void>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type AnchorElementProps = ButtonPropsWithoutHTML &
|
||||||
|
WithoutChildren<Omit<HTMLAnchorAttributes, 'href' | 'type'>> & {
|
||||||
|
href: HTMLAnchorAttributes['href'];
|
||||||
|
type?: never;
|
||||||
|
disabled?: HTMLButtonAttributes['disabled'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ButtonElementProps = ButtonPropsWithoutHTML &
|
||||||
|
WithoutChildren<Omit<HTMLButtonAttributes, 'type' | 'href'>> & {
|
||||||
|
type?: HTMLButtonAttributes['type'];
|
||||||
|
href?: never;
|
||||||
|
disabled?: HTMLButtonAttributes['disabled'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ButtonProps = AnchorElementProps | ButtonElementProps;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils/utils.js';
|
||||||
|
import { LoaderCircleIcon } from '@lucide/svelte';
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
variant = 'default',
|
||||||
|
size = 'default',
|
||||||
|
href = undefined,
|
||||||
|
type = 'button',
|
||||||
|
loading = false,
|
||||||
|
disabled = false,
|
||||||
|
tabindex = 0,
|
||||||
|
onclick,
|
||||||
|
onClickPromise,
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...rest
|
||||||
|
}: ButtonProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- This approach to disabled links is inspired by bits-ui see: https://github.com/huntabyte/bits-ui/pull/1055 -->
|
||||||
|
<svelte:element
|
||||||
|
this={href ? 'a' : 'button'}
|
||||||
|
{...rest}
|
||||||
|
data-slot="button"
|
||||||
|
type={href ? undefined : type}
|
||||||
|
href={href && !disabled ? href : undefined}
|
||||||
|
disabled={href ? undefined : disabled || loading}
|
||||||
|
aria-disabled={href ? disabled : undefined}
|
||||||
|
role={href && disabled ? 'link' : undefined}
|
||||||
|
tabindex={href && disabled ? -1 : tabindex}
|
||||||
|
class={cn(buttonVariants({ variant, size }), className)}
|
||||||
|
bind:this={ref}
|
||||||
|
onclick={async (
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
e: any
|
||||||
|
) => {
|
||||||
|
onclick?.(e);
|
||||||
|
|
||||||
|
if (type === undefined) return;
|
||||||
|
|
||||||
|
if (onClickPromise) {
|
||||||
|
loading = true;
|
||||||
|
|
||||||
|
await onClickPromise(e);
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{#if type !== undefined && loading}
|
||||||
|
<div class="absolute flex size-full place-items-center justify-center bg-inherit">
|
||||||
|
<div class="flex animate-spin place-items-center justify-center">
|
||||||
|
<LoaderCircleIcon class="size-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="sr-only">Loading</span>
|
||||||
|
{/if}
|
||||||
|
{@render children?.()}
|
||||||
|
</svelte:element>
|
||||||
27
src/lib/components/ui/button/index.ts
Normal file
27
src/lib/components/ui/button/index.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
Installed from @ieedan/shadcn-svelte-extras
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Root, {
|
||||||
|
type ButtonProps,
|
||||||
|
type ButtonSize,
|
||||||
|
type ButtonVariant,
|
||||||
|
type AnchorElementProps,
|
||||||
|
type ButtonElementProps,
|
||||||
|
type ButtonPropsWithoutHTML,
|
||||||
|
buttonVariants
|
||||||
|
} from './button.svelte';
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
type ButtonProps as Props,
|
||||||
|
//
|
||||||
|
Root as Button,
|
||||||
|
buttonVariants,
|
||||||
|
type ButtonProps,
|
||||||
|
type ButtonSize,
|
||||||
|
type ButtonVariant,
|
||||||
|
type AnchorElementProps,
|
||||||
|
type ButtonElementProps,
|
||||||
|
type ButtonPropsWithoutHTML
|
||||||
|
};
|
||||||
10
src/lib/components/ui/card/card-content.svelte
Normal file
10
src/lib/components/ui/card/card-content.svelte
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils/utils';
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
let { class: className, children, ...restProps }: HTMLAttributes<HTMLDivElement> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn('flex flex-col gap-2', className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
10
src/lib/components/ui/card/card-description.svelte
Normal file
10
src/lib/components/ui/card/card-description.svelte
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils/utils';
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
let { class: className, children, ...restProps }: HTMLAttributes<HTMLParagraphElement> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p class={cn('text-muted-foreground text-sm', className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</p>
|
||||||
10
src/lib/components/ui/card/card-header.svelte
Normal file
10
src/lib/components/ui/card/card-header.svelte
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils/utils';
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
let { class: className, children, ...restProps }: HTMLAttributes<HTMLDivElement> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn('flex flex-col gap-1', className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
10
src/lib/components/ui/card/card-title.svelte
Normal file
10
src/lib/components/ui/card/card-title.svelte
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils/utils';
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
let { class: className, children, ...restProps }: HTMLAttributes<HTMLHeadingElement> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h3 class={cn('text-lg font-semibold', className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</h3>
|
||||||
10
src/lib/components/ui/card/card.svelte
Normal file
10
src/lib/components/ui/card/card.svelte
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils/utils';
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
let { class: className, children, ...restProps }: HTMLAttributes<HTMLDivElement> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn('bg-card flex flex-col gap-4 rounded-lg border p-4', className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
7
src/lib/components/ui/card/index.ts
Normal file
7
src/lib/components/ui/card/index.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import Root from './card.svelte';
|
||||||
|
import Header from './card-header.svelte';
|
||||||
|
import Title from './card-title.svelte';
|
||||||
|
import Description from './card-description.svelte';
|
||||||
|
import Content from './card-content.svelte';
|
||||||
|
|
||||||
|
export { Root, Header, Title, Description, Content };
|
||||||
3
src/lib/components/ui/input/index.ts
Normal file
3
src/lib/components/ui/input/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Input from './input.svelte';
|
||||||
|
|
||||||
|
export { Input };
|
||||||
14
src/lib/components/ui/input/input.svelte
Normal file
14
src/lib/components/ui/input/input.svelte
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils/utils';
|
||||||
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
let { class: className, ...restProps }: HTMLInputAttributes = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input
|
||||||
|
class={cn(
|
||||||
|
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
7
src/lib/components/ui/light-switch/index.ts
Normal file
7
src/lib/components/ui/light-switch/index.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
Installed from @ieedan/shadcn-svelte-extras
|
||||||
|
*/
|
||||||
|
|
||||||
|
import LightSwitch from './light-switch.svelte';
|
||||||
|
|
||||||
|
export { LightSwitch };
|
||||||
18
src/lib/components/ui/light-switch/light-switch.svelte
Normal file
18
src/lib/components/ui/light-switch/light-switch.svelte
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!--
|
||||||
|
Installed from @ieedan/shadcn-svelte-extras
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { SunIcon, MoonIcon } from '@lucide/svelte';
|
||||||
|
import { toggleMode } from 'mode-watcher';
|
||||||
|
import { Button } from '$lib/components/ui/button/index.js';
|
||||||
|
import type { LightSwitchProps } from './types';
|
||||||
|
|
||||||
|
let { variant = 'outline' }: LightSwitchProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Button onclick={toggleMode} {variant} size="icon">
|
||||||
|
<SunIcon class="absolute scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
|
||||||
|
<MoonIcon class="scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
|
||||||
|
<span class="sr-only">Toggle theme</span>
|
||||||
|
</Button>
|
||||||
7
src/lib/components/ui/light-switch/types.ts
Normal file
7
src/lib/components/ui/light-switch/types.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
Installed from @ieedan/shadcn-svelte-extras
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type LightSwitchProps = {
|
||||||
|
variant?: 'outline' | 'ghost';
|
||||||
|
};
|
||||||
3
src/lib/components/ui/link/index.ts
Normal file
3
src/lib/components/ui/link/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Link from './link.svelte';
|
||||||
|
|
||||||
|
export { Link };
|
||||||
10
src/lib/components/ui/link/link.svelte
Normal file
10
src/lib/components/ui/link/link.svelte
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils/utils';
|
||||||
|
import type { HTMLAnchorAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
let { class: className, children, ...restProps }: HTMLAnchorAttributes = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a class={cn('text-primary', className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</a>
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
// place files you want to import through the `$lib` alias in this folder.
|
|
||||||
17
src/lib/utils/utils.ts
Normal file
17
src/lib/utils/utils.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
Installed from @ieedan/shadcn-svelte-extras
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { type ClassValue, clsx } 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> = T extends { child?: any } ? Omit<T, 'child'> : T;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, 'children'> : T;
|
||||||
|
export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
|
||||||
|
export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null };
|
||||||
|
|
@ -5,5 +5,5 @@
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ModeWatcher/>
|
<ModeWatcher />
|
||||||
{@render children()}
|
{@render children()}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,73 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { active } from '$lib/actions/active.svelte';
|
||||||
|
import Button from '$lib/components/ui/button/button.svelte';
|
||||||
|
import { LightSwitch } from '$lib/components/ui/light-switch';
|
||||||
|
import { ArrowLeftIcon } from '@lucide/svelte';
|
||||||
|
import { Avatar } from 'melt/components';
|
||||||
|
|
||||||
|
let { children } = $props();
|
||||||
|
|
||||||
|
const navigation: { title: string; href: string }[] = [
|
||||||
|
{
|
||||||
|
title: 'Account',
|
||||||
|
href: '/account'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Customization',
|
||||||
|
href: '/account/customization'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Models',
|
||||||
|
href: '/account/models'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'API Keys',
|
||||||
|
href: '/account/api-keys'
|
||||||
|
}
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="container mx-auto max-w-[1200px] space-y-8 pt-6 pb-24">
|
||||||
|
<header class="flex place-items-center justify-between px-4">
|
||||||
|
<a href="/chat" class="flex place-items-center gap-2 text-sm">
|
||||||
|
<ArrowLeftIcon class="size-4" />
|
||||||
|
Back to Chat
|
||||||
|
</a>
|
||||||
|
<div class="flex place-items-center gap-2">
|
||||||
|
<LightSwitch variant="ghost" />
|
||||||
|
<Button variant="ghost">Sign out</Button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="px-4 md:grid md:grid-cols-[280px_1fr]">
|
||||||
|
<div class="hidden md:col-start-1 md:block">
|
||||||
|
<div class="flex flex-col place-items-center gap-2">
|
||||||
|
<Avatar src="https://github.com/shadcn.png">
|
||||||
|
{#snippet children(avatar)}
|
||||||
|
<img {...avatar.image} alt="Avatar" class="size-40 rounded-full" />
|
||||||
|
<span {...avatar.fallback}>JD</span>
|
||||||
|
{/snippet}
|
||||||
|
</Avatar>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<h1 class="text-center text-2xl font-bold">John Doe</h1>
|
||||||
|
<span class="text-muted-foreground text-center text-sm">m@example.com</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-8 pl-12 md:col-start-2">
|
||||||
|
<div
|
||||||
|
class="bg-card text-muted-foreground gap-2 flex w-fit place-items-center rounded-lg p-1 text-sm"
|
||||||
|
>
|
||||||
|
{#each navigation as tab (tab)}
|
||||||
|
<a
|
||||||
|
href={tab.href}
|
||||||
|
use:active={{ activeForSubdirectories: false }}
|
||||||
|
class="data-[active=true]:bg-background data-[active=true]:text-foreground rounded-md px-2 py-1"
|
||||||
|
>
|
||||||
|
{tab.title}
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import * as Card from '$lib/components/ui/card';
|
||||||
|
import { KeyIcon } from '@lucide/svelte';
|
||||||
|
import { Input } from '$lib/components/ui/input';
|
||||||
|
import { Button } from '$lib/components/ui/button';
|
||||||
|
import { Link } from '$lib/components/ui/link';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Card.Root>
|
||||||
|
<Card.Header>
|
||||||
|
<Card.Title>
|
||||||
|
<KeyIcon class="inline size-4" /> Open Router
|
||||||
|
</Card.Title>
|
||||||
|
<Card.Description>API Key for OpenRouter.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Content>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<Input type="password" placeholder="sk-or-..." />
|
||||||
|
<span class="text-muted-foreground text-xs">
|
||||||
|
Get your API key from
|
||||||
|
<Link href="https://openrouter.ai/settings/keys" target="_blank" class="text-blue-500">
|
||||||
|
OpenRouter
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</div>
|
||||||
|
</Card.Content>
|
||||||
|
</Card.Root>
|
||||||
Loading…
Add table
Reference in a new issue