Moved to ReactJS

This commit is contained in:
Alois 2026-03-14 12:35:30 +01:00
commit 1d0ebeb2b7
100 changed files with 1909 additions and 6056 deletions

510
bun.lock
View file

@ -7,9 +7,11 @@
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/node": "^25.4.0",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@typescript-eslint/parser": "^8.57.0",
"eslint": "^10.0.3",
"eslint-plugin-solid": "^0.14.5",
"eslint-plugin-react-hooks": "^5.2.0",
"globals": "^17.4.0",
"prettier": "^3.8.1",
"typescript": "^5.9.3",
@ -20,9 +22,9 @@
"name": "@tensamin/chat",
"version": "0.0.0",
"dependencies": {
"@solidjs/router": "^0.15.4",
"@tanstack/solid-query": "^5.90.23",
"@tanstack/solid-virtual": "^3.13.21",
"@tanstack/react-query": "^5.0.0",
"@tanstack/react-router": "^1.0.0",
"@tanstack/react-virtual": "^3.0.0",
"@tensamin/core-crypto": "workspace:*",
"@tensamin/core-storage": "workspace:*",
"@tensamin/core-user": "workspace:*",
@ -30,9 +32,9 @@
"@tensamin/shared": "workspace:*",
"@tensamin/ttp": "workspace:*",
"@tensamin/ui": "workspace:*",
"lucide-solid": "^0.564.0",
"solid-js": "^1.9.11",
"solid-sonner": "^0.2.8",
"lucide-react": "^0.564.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"zod": "^4.3.6",
},
},
@ -43,7 +45,8 @@
"@noble/curves": "^2.0.1",
"@tensamin/ui": "workspace:*",
"comlink": "^4.4.2",
"solid-js": "^1.9.11",
"react": "^19.2.0",
"react-dom": "^19.2.0",
},
},
"packages/core/markdown": {
@ -55,7 +58,8 @@
"@codemirror/state": "^6.5.4",
"@codemirror/view": "^6.39.15",
"@tensamin/ui": "workspace:*",
"solid-js": "^1.9.11",
"react": "^19.2.0",
"react-dom": "^19.2.0",
},
},
"packages/core/storage": {
@ -64,19 +68,21 @@
"dependencies": {
"@tensamin/shared": "workspace:*",
"@tensamin/ui": "workspace:*",
"solid-js": "^1.9.11",
"react": "^19.2.0",
"react-dom": "^19.2.0",
},
},
"packages/core/ttp": {
"name": "@tensamin/ttp",
"version": "0.0.0",
"dependencies": {
"@solidjs/router": "^0.15.4",
"@tanstack/react-router": "^1.0.0",
"@tensamin/core-crypto": "workspace:*",
"@tensamin/core-storage": "workspace:*",
"@tensamin/shared": "workspace:*",
"@tensamin/ui": "workspace:*",
"solid-js": "^1.9.10",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"zod": "^4.3.6",
},
"devDependencies": {
@ -89,7 +95,8 @@
"dependencies": {
"@tensamin/shared": "workspace:*",
"@tensamin/ttp": "workspace:*",
"solid-js": "^1.9.11",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"zod": "^4.3.6",
},
},
@ -108,11 +115,9 @@
"name": "@tensamin/notifications",
"version": "0.0.0",
"dependencies": {
"@solidjs/router": "^0.15.4",
"@tensamin/ttp": "git+https://github.com/Tensamin/TTP.git",
"lucide-solid": "^0.564.0",
"solid-js": "^1.9.11",
"solid-sonner": "^0.2.8",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"zod": "^4.3.6",
},
},
@ -120,9 +125,10 @@
"name": "@tensamin/shared",
"version": "0.0.0",
"dependencies": {
"lucide-solid": "^0.564.0",
"solid-js": "^1.9.11",
"solid-sonner": "^0.2.8",
"lucide-react": "^0.564.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"sonner": "^1.0.0",
"zod": "^4.3.6",
},
},
@ -130,19 +136,13 @@
"name": "@tensamin/ui",
"version": "0.0.0",
"dependencies": {
"@ark-ui/solid": "^5.34.1",
"@corvu/drawer": "^0.2.4",
"@corvu/otp-field": "^0.1.4",
"@corvu/resizable": "^0.2.5",
"@kobalte/core": "^0.13.11",
"@tanstack/solid-virtual": "^3.13.21",
"@radix-ui/react-context-menu": "^2.2.16",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk-solid": "^1.1.2",
"embla-carousel-solid": "^8.6.0",
"lucide-solid": "^0.564.0",
"solid-js": "^1.9.11",
"solid-sonner": "^0.2.8",
"lucide-react": "^0.564.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"sonner": "^1.0.0",
"tailwind-merge": "^3.5.0",
},
},
@ -150,15 +150,10 @@
"name": "@tensamin/web",
"version": "0.0.0",
"dependencies": {
"@ark-ui/solid": "^5.34.1",
"@corvu/drawer": "^0.2.4",
"@corvu/otp-field": "^0.1.4",
"@corvu/resizable": "^0.2.5",
"@kobalte/core": "^0.13.11",
"@noble/curves": "^2.0.1",
"@solidjs/router": "^0.15.4",
"@tailwindcss/vite": "^4.2.1",
"@tanstack/solid-virtual": "^3.13.21",
"@tanstack/react-router": "^1.0.0",
"@tanstack/react-virtual": "^3.0.0",
"@tensamin/chat": "workspace:*",
"@tensamin/core-crypto": "workspace:*",
"@tensamin/core-storage": "workspace:*",
@ -168,12 +163,11 @@
"@tensamin/ui": "workspace:*",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk-solid": "^1.1.2",
"comlink": "^4.4.2",
"embla-carousel-solid": "^8.6.0",
"lucide-solid": "^0.564.0",
"solid-js": "^1.9.11",
"solid-sonner": "^0.2.8",
"lucide-react": "^0.564.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"sonner": "^1.0.0",
"tailwind-merge": "^3.5.0",
"tailwind-scrollbar-hide": "^4.0.0",
"tailwindcss": "^4.2.1",
@ -183,20 +177,18 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@vitejs/plugin-react": "^5.0.4",
"eslint": "^10.0.3",
"eslint-plugin-solid": "^0.14.5",
"globals": "^17.4.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.57.0",
"vite": "^7.3.1",
"vite-plugin-lucide-preprocess": "^1.4.8",
"vite-plugin-solid": "^2.11.10",
},
},
},
"packages": {
"@ark-ui/solid": ["@ark-ui/solid@5.34.1", "", { "dependencies": { "@internationalized/date": "3.11.0", "@zag-js/accordion": "1.35.3", "@zag-js/anatomy": "1.35.3", "@zag-js/angle-slider": "1.35.3", "@zag-js/async-list": "1.35.3", "@zag-js/auto-resize": "1.35.3", "@zag-js/avatar": "1.35.3", "@zag-js/carousel": "1.35.3", "@zag-js/cascade-select": "1.35.3", "@zag-js/checkbox": "1.35.3", "@zag-js/clipboard": "1.35.3", "@zag-js/collapsible": "1.35.3", "@zag-js/collection": "1.35.3", "@zag-js/color-picker": "1.35.3", "@zag-js/color-utils": "1.35.3", "@zag-js/combobox": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/date-picker": "1.35.3", "@zag-js/date-utils": "1.35.3", "@zag-js/dialog": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/drawer": "1.35.3", "@zag-js/editable": "1.35.3", "@zag-js/file-upload": "1.35.3", "@zag-js/file-utils": "1.35.3", "@zag-js/floating-panel": "1.35.3", "@zag-js/focus-trap": "1.35.3", "@zag-js/highlight-word": "1.35.3", "@zag-js/hover-card": "1.35.3", "@zag-js/i18n-utils": "1.35.3", "@zag-js/image-cropper": "1.35.3", "@zag-js/json-tree-utils": "1.35.3", "@zag-js/listbox": "1.35.3", "@zag-js/marquee": "1.35.3", "@zag-js/menu": "1.35.3", "@zag-js/navigation-menu": "1.35.3", "@zag-js/number-input": "1.35.3", "@zag-js/pagination": "1.35.3", "@zag-js/password-input": "1.35.3", "@zag-js/pin-input": "1.35.3", "@zag-js/popover": "1.35.3", "@zag-js/presence": "1.35.3", "@zag-js/progress": "1.35.3", "@zag-js/qr-code": "1.35.3", "@zag-js/radio-group": "1.35.3", "@zag-js/rating-group": "1.35.3", "@zag-js/scroll-area": "1.35.3", "@zag-js/select": "1.35.3", "@zag-js/signature-pad": "1.35.3", "@zag-js/slider": "1.35.3", "@zag-js/solid": "1.35.3", "@zag-js/splitter": "1.35.3", "@zag-js/steps": "1.35.3", "@zag-js/switch": "1.35.3", "@zag-js/tabs": "1.35.3", "@zag-js/tags-input": "1.35.3", "@zag-js/timer": "1.35.3", "@zag-js/toast": "1.35.3", "@zag-js/toggle": "1.35.3", "@zag-js/toggle-group": "1.35.3", "@zag-js/tooltip": "1.35.3", "@zag-js/tour": "1.35.3", "@zag-js/tree-view": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" }, "peerDependencies": { "solid-js": ">=1.6.0" } }, "sha512-DMTAVtmibwcKC9B3iMhQfXfTvCsQYs43qTX2VZPArVsjQGNZlDXK3+oPmaw1FPoVBD5csi1bjGNkwruhbh1gAA=="],
"@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
"@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="],
@ -225,7 +217,9 @@
"@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="],
"@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="],
"@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
"@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
@ -255,16 +249,6 @@
"@codemirror/view": ["@codemirror/view@6.39.16", "", { "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-m6S22fFpKtOWhq8HuhzsI1WzUP/hB9THbDj0Tl5KX4gbO6Y91hwBl7Yky33NdvB6IffuRFiBxf1R8kJMyXmA4Q=="],
"@corvu/dialog": ["@corvu/dialog@0.2.4", "", { "dependencies": { "@corvu/utils": "~0.4.2", "solid-dismissible": "~0.1.1", "solid-focus-trap": "~0.1.8", "solid-presence": "~0.2.0", "solid-prevent-scroll": "~0.1.10" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-n54vJq+fOy8GVrnYBdJpD6JXNuyx7LOeMrRxwzAvZnYGpW8+AA12tnb/P/2emJj/HjOO5otheGKb0breshdFlA=="],
"@corvu/drawer": ["@corvu/drawer@0.2.4", "", { "dependencies": { "@corvu/dialog": "~0.2.4", "@corvu/utils": "~0.4.2", "@solid-primitives/memo": "^1.4.1", "solid-transition-size": "~0.1.4" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-7jQoGZ8ROB9CmXam2nMY2wEskU3IoFwZQywkF/7vrBc/edGsPv7mOVQ1GN6G+4nd7nrMZ3UtqHAhmRh9V0azlw=="],
"@corvu/otp-field": ["@corvu/otp-field@0.1.4", "", { "dependencies": { "@corvu/utils": "~0.4.2" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-3eG7OoUt6CfVqGujIYfqImdrhGR/s4DpKr5ZQT10zzw3nawIlcwVpqoHTam0v4cgv+NXXvl6I8DoA3J+WgW2YA=="],
"@corvu/resizable": ["@corvu/resizable@0.2.5", "", { "dependencies": { "@corvu/utils": "~0.4.2" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-psax38DraM9SXtaNh/sfvizGO6xLCL4sqT4rWYlMZP5ZsUM05Q30tvXgnTo2roQFNOz2e06WofhDsBXTjY6g4A=="],
"@corvu/utils": ["@corvu/utils@0.4.2", "", { "dependencies": { "@floating-ui/dom": "^1.6.11" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-Ox2kYyxy7NoXdKWdHeDEjZxClwzO4SKM8plAaVwmAJPxHMqA0rLOoAsa+hBDwRLpctf+ZRnAd/ykguuJidnaTA=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="],
@ -337,6 +321,8 @@
"@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="],
"@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.8", "", { "dependencies": { "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A=="],
"@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
@ -347,10 +333,6 @@
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
"@internationalized/date": ["@internationalized/date@3.11.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-BOx5huLAWhicM9/ZFs84CzP+V3gBW6vlpM02yzsdYC7TGlZJX1OJiEEHcSayF00Z+3jLlm4w79amvSt6RqKN3Q=="],
"@internationalized/number": ["@internationalized/number@3.6.5", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
@ -361,10 +343,6 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@kobalte/core": ["@kobalte/core@0.13.11", "", { "dependencies": { "@floating-ui/dom": "^1.5.1", "@internationalized/date": "^3.4.0", "@internationalized/number": "^3.2.1", "@kobalte/utils": "^0.9.1", "@solid-primitives/props": "^3.1.8", "@solid-primitives/resize-observer": "^2.0.26", "solid-presence": "^0.1.8", "solid-prevent-scroll": "^0.1.4" }, "peerDependencies": { "solid-js": "^1.8.15" } }, "sha512-hK7TYpdib/XDb/r/4XDBFaO9O+3ZHz4ZWryV4/3BfES+tSQVgg2IJupDnztKXB0BqbSRy/aWlHKw1SPtNPYCFQ=="],
"@kobalte/utils": ["@kobalte/utils@0.9.1", "", { "dependencies": { "@solid-primitives/event-listener": "^2.2.14", "@solid-primitives/keyed": "^1.2.0", "@solid-primitives/map": "^0.4.7", "@solid-primitives/media": "^2.2.4", "@solid-primitives/props": "^3.1.8", "@solid-primitives/refs": "^1.0.5", "@solid-primitives/utils": "^6.2.1" }, "peerDependencies": { "solid-js": "^1.8.8" } }, "sha512-eeU60A3kprIiBDAfv9gUJX1tXGLuZiKMajUfSQURAF2pk4ZoMYiqIzmrMBvzcxP39xnYttgTyQEVLwiTZnrV4w=="],
"@lezer/common": ["@lezer/common@1.5.1", "", {}, "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw=="],
"@lezer/css": ["@lezer/css@1.3.1", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.3.0" } }, "sha512-PYAKeUVBo3HFThruRyp/iK91SwiZJnzXh8QzkQlwijB5y+N5iB28+iLk78o2zmKqqV0uolNhCwFqB8LA7b0Svg=="],
@ -387,6 +365,60 @@
"@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="],
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
"@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
"@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="],
"@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
"@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww=="],
"@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
"@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="],
"@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="],
"@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="],
"@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="],
"@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
"@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="],
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
"@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
"@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="],
"@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="],
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="],
@ -437,40 +469,6 @@
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="],
"@solid-primitives/deep": ["@solid-primitives/deep@0.2.10", "", { "dependencies": { "@solid-primitives/memo": "^1.3.10" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-uzhXr/cMABsGkTcV3yuviZjTDmj44wb2JetuJ5O8NCugz4Ug/6XlPjT8ISMPYV7eCbC/gFhgt7e60+TRpl2Upg=="],
"@solid-primitives/event-listener": ["@solid-primitives/event-listener@2.4.5", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-nwRV558mIabl4yVAhZKY8cb6G+O1F0M6Z75ttTu5hk+SxdOnKSGj+eetDIu7Oax1P138ZdUU01qnBPR8rnxaEA=="],
"@solid-primitives/keyed": ["@solid-primitives/keyed@1.5.3", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-zNadtyYBhJSOjXtogkGHmRxjGdz9KHc8sGGVAGlUABkE8BED2tbIZoxkwSqzOwde8OcUEH0bb5DLZUWIMvyBSA=="],
"@solid-primitives/map": ["@solid-primitives/map@0.4.13", "", { "dependencies": { "@solid-primitives/trigger": "^1.1.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-B1zyFbsiTQvqPr+cuPCXO72sRuczG9Swncqk5P74NCGw1VE8qa/Ry9GlfI1e/VdeQYHjan+XkbE3rO2GW/qKew=="],
"@solid-primitives/media": ["@solid-primitives/media@2.3.5", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.5", "@solid-primitives/rootless": "^1.5.3", "@solid-primitives/static-store": "^0.1.3", "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-LX9fB5WDaK87FMDtUB1qokBOfT2et9Uobv/zZaKLH9caFSz4+P70MBKEIBHcZQy+9MV5M2XvGYLTbLskjkzMjA=="],
"@solid-primitives/memo": ["@solid-primitives/memo@1.4.5", "", { "dependencies": { "@solid-primitives/scheduled": "^1.5.3", "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-dMfFShNsyX5virETyDv/Uoy2HP+PL4k8cUTTLb2r4TfoqJb010KIaOuURqp/Qbdznp4ZkDuP57b28d45kaOueQ=="],
"@solid-primitives/mutation-observer": ["@solid-primitives/mutation-observer@1.2.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-p69O/zwvR7fCT87KGN3hkDfsZyWAJH+bsCZrdmWHcM5/8lg1Ndh7NjxXX1G1Ugk6ru/Bx6MvSppbbfkdBlwzgQ=="],
"@solid-primitives/props": ["@solid-primitives/props@3.2.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-XzG6en9gSFwmvbKcATm2BxL63HegZ+BAG5fmHi8jyBppQHcaths7ffz+6vYvwYy3nlgLa20ufJLj7tst+PcHFA=="],
"@solid-primitives/refs": ["@solid-primitives/refs@1.1.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-aam02fjNKpBteewF/UliPSQCVJsIIGOLEWQOh+ll6R/QePzBOOBMcC4G+5jTaO75JuUS1d/14Q1YXT3X0Ow6iA=="],
"@solid-primitives/resize-observer": ["@solid-primitives/resize-observer@2.1.5", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.5", "@solid-primitives/rootless": "^1.5.3", "@solid-primitives/static-store": "^0.1.3", "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-AiyTknKcNBaKHbcSMuxtSNM8FjIuiSuFyFghdD0TcCMU9hKi9EmsC5pjfjDwxE+5EueB1a+T/34PLRI5vbBbKw=="],
"@solid-primitives/rootless": ["@solid-primitives/rootless@1.5.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA=="],
"@solid-primitives/scheduled": ["@solid-primitives/scheduled@1.5.3", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-oNwLE6E6lxJAWrc8QXuwM0k2oU1BnANnkChwMw82aK1j3+mWGJkG1IFe5gCwbV+afYmjI76t9JJV3md/8tLw+g=="],
"@solid-primitives/static-store": ["@solid-primitives/static-store@0.1.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-uxez7SXnr5GiRnzqO2IEDjOJRIXaG+0LZLBizmUA1FwSi+hrpuMzVBwyk70m4prcl8X6FDDXUl9O8hSq8wHbBQ=="],
"@solid-primitives/trigger": ["@solid-primitives/trigger@1.2.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-Za2JebEiDyfamjmDwRaESYqBBYOlgYGzB8kHYH0QrkXyLf2qNADlKdGN+z3vWSLCTDcKxChS43Kssjuc0OZhng=="],
"@solid-primitives/utils": ["@solid-primitives/utils@6.4.0", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-AeGTBg8Wtkh/0s+evyLtP8piQoS4wyqqQaAFs2HJcFMMjYAtUgo+ZPduRXLjPlqKVc2ejeR544oeqpbn8Egn8A=="],
"@solidjs/router": ["@solidjs/router@0.15.4", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-WOpgg9a9T638cR+5FGbFi/IV4l2FpmBs1GpIMSPa0Ce9vyJN7Wts+X2PqMf9IYn0zUj2MlSJtm1gp7/HI/n5TQ=="],
"@swc/helpers": ["@swc/helpers@0.5.19", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA=="],
"@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="],
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="],
@ -501,13 +499,23 @@
"@tailwindcss/vite": ["@tailwindcss/vite@4.2.1", "", { "dependencies": { "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "tailwindcss": "4.2.1" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w=="],
"@tanstack/history": ["@tanstack/history@1.161.4", "", {}, "sha512-Kp/WSt411ZWYvgXy6uiv5RmhHrz9cAml05AQPrtdAp7eUqvIDbMGPnML25OKbzR3RJ1q4wgENxDTvlGPa9+Mww=="],
"@tanstack/query-core": ["@tanstack/query-core@5.90.20", "", {}, "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg=="],
"@tanstack/solid-query": ["@tanstack/solid-query@5.90.23", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "solid-js": "^1.6.0" } }, "sha512-pbZc4+Kgm7ktzIuu01R3KOWfazQKgNp4AZvW0RSvv+sNMpYoileUDAkXEcjDJe6RJmb3fVvTR4LlcSL5pxDElQ=="],
"@tanstack/react-query": ["@tanstack/react-query@5.90.21", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg=="],
"@tanstack/solid-virtual": ["@tanstack/solid-virtual@3.13.21", "", { "dependencies": { "@tanstack/virtual-core": "3.13.21" }, "peerDependencies": { "solid-js": "^1.3.0" } }, "sha512-SOwgCLVFHzousW9TnbkRMlUxdCHWP8xmp+v9ay2S6aaNT4MgEp6w3/EAFPQuXGVoRgyh8i4M+KZKpmliyTsNfw=="],
"@tanstack/react-router": ["@tanstack/react-router@1.167.0", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/react-store": "^0.9.1", "@tanstack/router-core": "1.167.0", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-U7CamtXjuC8ixg1c32Rj/4A2OFBnjtMLdbgbyOGHrFHE7ULWS/yhnZLVXff0QSyn6qF92Oecek9mDMHCaTnB2Q=="],
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.21", "", {}, "sha512-ww+fmLHyCbPSf7JNbWZP3g7wl6SdNo3ah5Aiw+0e9FDErkVHLKprYUrwTm7dF646FtEkN/KkAKPYezxpmvOjxw=="],
"@tanstack/react-store": ["@tanstack/react-store@0.9.2", "", { "dependencies": { "@tanstack/store": "0.9.2", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Vt5usJE5sHG/cMechQfmwvwne6ktGCELe89Lmvoxe3LKRoFrhPa8OCKWs0NliG8HTJElEIj7PLtaBQIcux5pAQ=="],
"@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.22", "", { "dependencies": { "@tanstack/virtual-core": "3.13.22" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-EaOrBBJLi3M0bTMQRjGkxLXRw7Gizwntoy5E2Q2UnSbML7Mo2a1P/Hfkw5tw9FLzK62bj34Jl6VNbQfRV6eJcA=="],
"@tanstack/router-core": ["@tanstack/router-core@1.167.0", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/store": "^0.9.1", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-pnaaUP+vMQEyL2XjZGe2PXmtzulxvXfGyvEMUs+AEBaNEk77xWA88bl3ujiBRbUxzpK0rxfJf+eSKPdZmBMFdQ=="],
"@tanstack/store": ["@tanstack/store@0.9.2", "", {}, "sha512-K013lUJEFJK2ofFQ/hZKJUmCnpcV00ebLyOyFOWQvyQHUOZp/iYO84BM6aOGiV81JzwbX0APTVmW8YI7yiG5oA=="],
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.22", "", {}, "sha512-isuUGKsc5TAPDoHSbWTbl1SCil54zOS2MiWz/9GCWHPUQOvNTQx8qJEWC7UWR0lShhbK0Lmkcf0SZYxvch7G3g=="],
"@tensamin/chat": ["@tensamin/chat@workspace:packages/chat"],
@ -547,6 +555,10 @@
"@types/node": ["@types/node@25.4.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw=="],
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.57.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/type-utils": "8.57.0", "@typescript-eslint/utils": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.57.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.57.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g=="],
@ -563,157 +575,11 @@
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.57.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.57.0", "@typescript-eslint/tsconfig-utils": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.56.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg=="],
"@zag-js/accordion": ["@zag-js/accordion@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-wmw6yo5Zr6ShiKGTc5ICEOJCurWAOSGubIpGISiHi3cZ4tlxKF/vpATIUT3eq8xzdB56YK57yKCujs/WmwqqoA=="],
"@zag-js/anatomy": ["@zag-js/anatomy@1.35.3", "", {}, "sha512-oqU9iLNNylrtJMBX5Xu4DsxnPNvtZLiobryv2oNtsDI1mi1Fca/XHghQC9K5aYT0qNsmHj1M3W5WAWTaOtPLkQ=="],
"@zag-js/angle-slider": ["@zag-js/angle-slider@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/rect-utils": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-HXRlmsbNEJSBT53fq9XQKL/vwZWwJC3nprskI7s4f/jy8a4uXPTlv7N7zuBYjew+ScTMzZah6fLWzUztBehmSg=="],
"@zag-js/aria-hidden": ["@zag-js/aria-hidden@1.35.3", "", { "dependencies": { "@zag-js/dom-query": "1.35.3" } }, "sha512-dk5POebn10WneQfLrEgbTzwolaXWpCSHL6F3jCTinW9IbOx7BXghzJD21iU5Iun+y9CorqJPW3p7LplYNUMO5Q=="],
"@zag-js/async-list": ["@zag-js/async-list@1.35.3", "", { "dependencies": { "@zag-js/core": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-SXX3wGzLK/maKS1PJ3XfLIGWbu0022f/OhcFsT1PbiHnoFZTH7h2fBhirrCBfy2TYFQ6r5uxgjkhPUNkuaeYnA=="],
"@zag-js/auto-resize": ["@zag-js/auto-resize@1.35.3", "", { "dependencies": { "@zag-js/dom-query": "1.35.3" } }, "sha512-ufG8HSqzLd9h5rnos8aumj8iORlRskeR/gbpJu1NHrnHBWIrpuXm6KJJR2oZhTFY1BUMMk8eYIBA2QkVuiJzWA=="],
"@zag-js/avatar": ["@zag-js/avatar@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-lbQ2Q4Va8AAScKULOHw2tCQez+0JRYGHSMFq6i+dJmeT3dlSgRanm69ra6K2po6hM9E4v6pRe+xOVE+9QMDnuA=="],
"@zag-js/carousel": ["@zag-js/carousel@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/scroll-snap": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-F+b8HzUeZfB+xUkAkLG4r0Ubui8pj7pSgZhi26ZiWgsM7tsd7cD+xRMXkvPEITN5Fd5QCe3KlVBuE00w5byjmg=="],
"@zag-js/cascade-select": ["@zag-js/cascade-select@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/collection": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-visible": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/rect-utils": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-Nifdx77hEuAdXqr1wpZSPjLXqygRhq/WvnPjGhCeSqFPpy62uT4JZ3avyjUZ4I0UhvIpkleUcXtFwQ3cSMh4ww=="],
"@zag-js/checkbox": ["@zag-js/checkbox@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-visible": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-8XBt/Wg2zSQWqV2ZFqZBQUjYRkOYHA2O3IEi0VVYtds3S1n7Pu/HqkZT5qDw+E/SY2+X9Uyx4hO7h2XrlsiZQQ=="],
"@zag-js/clipboard": ["@zag-js/clipboard@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-obTwynBpp6c17fLHe5tg//FQ497QsyCEry+K3bTdlrivWW200wvfHxZ6RKVbKwDAwhH+ye0bI1xkYAId8j7sdA=="],
"@zag-js/collapsible": ["@zag-js/collapsible@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-IweG8JOBCerJwLO6QzTZGEMlsYUmQfQSeD0jniFguMM8vcunvGVSrM+AaL8pDbmXd+snXokaGyJpGO3vzMW6Fw=="],
"@zag-js/collection": ["@zag-js/collection@1.35.3", "", { "dependencies": { "@zag-js/utils": "1.35.3" } }, "sha512-BYoWJ4b7ma2PgiuQbRSnP603f2DlK6se5JtViUHTamZScLLLWnWHuQ6zFa1KS5kiIkbb7CFM6/bJ3WNYLch8Ig=="],
"@zag-js/color-picker": ["@zag-js/color-picker@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/color-utils": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-i9roSgtqeA1b4Q+jWqnxjXB//BQXMP5m1FQ4YcZVq/0yT14A53JIknchuqrh3wC3yPsJMXFqCoKg+NET2+OVig=="],
"@zag-js/color-utils": ["@zag-js/color-utils@1.35.3", "", { "dependencies": { "@zag-js/utils": "1.35.3" } }, "sha512-vxkEVgz4YdSbdaPvjiRI1VsJAdwzu/dUNvzqOaiVcPDrHr/FFgmUbv0SOFjnfSb2QWGI8EDEMn02RW9ym+BzGw=="],
"@zag-js/combobox": ["@zag-js/combobox@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/aria-hidden": "1.35.3", "@zag-js/collection": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-visible": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-s1qmttTGJTMjlDakL+uvWSEggpafKr1vhOeZCh8j+N4eFt9bLAwaffjuh/1JzWBvzovw7WoMVkizdTXPlN8oYg=="],
"@zag-js/core": ["@zag-js/core@1.35.3", "", { "dependencies": { "@zag-js/dom-query": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-fGAHyqOYSEFmo52t7wI4dvbFfLyJmUlyf7wknsiUlzUHlrn3yv5PAZYZ2TibpOD1hwXIp4AoCjbiIPPZBxirZw=="],
"@zag-js/date-picker": ["@zag-js/date-picker@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/date-utils": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/live-region": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" }, "peerDependencies": { "@internationalized/date": ">=3.0.0" } }, "sha512-4G10h6pzzLbd84SE2CKtqi6Z9wEBhSyx4GRSxxy3tsf5wAxnz4anRFat9CGwn2YVUYcUJpD+umYgBMPt6zGDnA=="],
"@zag-js/date-utils": ["@zag-js/date-utils@1.35.3", "", { "peerDependencies": { "@internationalized/date": ">=3.0.0" } }, "sha512-1co0FPpZ6nO5dN8sZtECkMYaf+3E5zu0KSIJZpZiXb4TgsZMDyHu7K7IsiKFHk9qmhuF6AdPpNxBju91pSXMFg=="],
"@zag-js/dialog": ["@zag-js/dialog@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/aria-hidden": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-trap": "1.35.3", "@zag-js/remove-scroll": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-byosV+aBHH5LoFKnjEgC7WdqJid7bP9UhgWLSC7+IXbxrif9Czg1YVp6ZlQM6Nx6uD1vnty4touI3P7D7CTKcw=="],
"@zag-js/dismissable": ["@zag-js/dismissable@1.35.3", "", { "dependencies": { "@zag-js/dom-query": "1.35.3", "@zag-js/interact-outside": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-XPk+lqmsZp2Z1yMb5K1yj/e7Sobv4D7zK66B1GS97lk9Xzz8vuSgsimcLy0p7RXQl3KL6H5L69inSuQa2exybQ=="],
"@zag-js/dom-query": ["@zag-js/dom-query@1.35.3", "", { "dependencies": { "@zag-js/types": "1.35.3" } }, "sha512-1RbFZoT4CjlHN9TUNse1++ZVOyKo45ktucTIT349o6HMsoWWKmTJDPvFkMBbmu/qY6XXn4dT+LJEp4bL3DR+Qw=="],
"@zag-js/drawer": ["@zag-js/drawer@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/aria-hidden": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-trap": "1.35.3", "@zag-js/remove-scroll": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-DN5bwa7bDCDaUSbNzFxMc2U/WmbLcXvPSQjyOpKI6CC3VbW2kKaOnjJ5qQG+W5YBO0FpmJBtaxRV7lke4sZH2w=="],
"@zag-js/editable": ["@zag-js/editable@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/interact-outside": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-HcjeacS61vQXfNT9IalZj/+oS45yW5bIDO2NjJWV7zNe5AG29NCceUnvBhy+hrUKPnKcjfDocdW5rCL+Lvs/CQ=="],
"@zag-js/file-upload": ["@zag-js/file-upload@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/file-utils": "1.35.3", "@zag-js/i18n-utils": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-oIYwnDct4ERo2mfmcxsBIJnlmpzjrzYx82SQsXWD3NGKx3cgdh2lwBX+ebItaLH1jkgzBa3z0TWxc6rfvcUXbw=="],
"@zag-js/file-utils": ["@zag-js/file-utils@1.35.3", "", { "dependencies": { "@zag-js/i18n-utils": "1.35.3" } }, "sha512-Tb05RCzx4swc156hd4jLiO7z+Gxg/HQ+JCds03jgTbrFJAz2D56YaMeI7gSDc1m4Xre3nyqQpSo9AeX5nzbE/w=="],
"@zag-js/floating-panel": ["@zag-js/floating-panel@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/rect-utils": "1.35.3", "@zag-js/store": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-nTZypcS0X46Oo1kpCQTnP5UlzjhypOAj3B4dq2z/3bAOC0TntYTnFkj8PbEJtExk7364xfMyxfgZOiv7Aqq01w=="],
"@zag-js/focus-trap": ["@zag-js/focus-trap@1.35.3", "", { "dependencies": { "@zag-js/dom-query": "1.35.3" } }, "sha512-evErLlGFdDVCI8xipNS5k0rAvO+KFRA9g273bbfWAL1+mT54mcB/XHa85nC3QpPgMNrSh+6LUNq9fapyOGoyYg=="],
"@zag-js/focus-visible": ["@zag-js/focus-visible@1.35.3", "", { "dependencies": { "@zag-js/dom-query": "1.35.3" } }, "sha512-g4F8PRGIoFoKBrHiQ1HQh5AjCS7brFRXHvpbDNb9+T11FGlF5Turb+6OVRoNV8MmiuqMltO2I28l36YsGc//uQ=="],
"@zag-js/highlight-word": ["@zag-js/highlight-word@1.35.3", "", {}, "sha512-K+mvEBbf3SUFjQeMeJQYb3cjri3x6sPaPhcKWayalelSLB/StWEGqcpmz+a6uUYrCUAK5kEi3Hn0YLGfn0GOig=="],
"@zag-js/hover-card": ["@zag-js/hover-card@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-xVoKOtvrnzhYzciZ1csgiV76IQ4DRtx1lsJeFSrfg5MH0kYWeC/pcmm3yCd2+Qh/45J7DbSXeZneqxpyiF5Vvw=="],
"@zag-js/i18n-utils": ["@zag-js/i18n-utils@1.35.3", "", { "dependencies": { "@zag-js/dom-query": "1.35.3" } }, "sha512-k7UcNxbnC2jvGwCoHYAkFD3ZaRSMQNVHfuy8TujZQ+ci3IJovwgWLveZoRfFbXHkTLfhmbpE2tFXBdpwOVZutg=="],
"@zag-js/image-cropper": ["@zag-js/image-cropper@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-1PH6bg8JAQESHzNqjka2TJ0QGNBGBAO6rb7AZ+9CaCCLw0pIzbUJhqPMkwd9GhdWGKGP+e7wFitnjcT4W5Js8g=="],
"@zag-js/interact-outside": ["@zag-js/interact-outside@1.35.3", "", { "dependencies": { "@zag-js/dom-query": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-tOcuo/IztzpU7UKXtjVrLZtXzzcbhP4n2WynKwDRkTkq3mRCp61xXJp1csIBycI3JHm/CMeAEcPdRIioxIT/Zw=="],
"@zag-js/json-tree-utils": ["@zag-js/json-tree-utils@1.35.3", "", {}, "sha512-nOv2dPJf+1mxsobYiSlYt96hR1MK7iHKG1iDLoO5wLggS6GQA3ix1BerHJK0zdehoEZ71R45el5ghCG1HB9VzQ=="],
"@zag-js/listbox": ["@zag-js/listbox@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/collection": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-visible": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-FE6FOuBr6aWtOb8U8oDvAvcUzD6JKLXAe8WngiLFG+b2yyW4nlaz2AcKRG1bjjB066UMxMo9/+2p4D0Kf5Id1Q=="],
"@zag-js/live-region": ["@zag-js/live-region@1.35.3", "", {}, "sha512-64rWcfggYpyr2Fn4pdrB/lljMgm3quwn9is+vdDN85Vv3WShKWoz08T4njidm0hwcIbzas0bRqQYWDLLsAoSJQ=="],
"@zag-js/marquee": ["@zag-js/marquee@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-bKZVpmAJWPDORP7WOWnS+65W5ZQBQmRs8zvV33ZfCpFbkXjhRiqKSzIj223/VOc2NEDjyWagz2vioAxrFYVzww=="],
"@zag-js/menu": ["@zag-js/menu@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-visible": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/rect-utils": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-KyY0EZXkIU57Mjt+Lg+pupiePk3LcnQcB3Gl05Vva61bNjBjdKV71qwCQru/OxPZEwYgPo46L7TDIb56kfK/VQ=="],
"@zag-js/navigation-menu": ["@zag-js/navigation-menu@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-8cCHx0X/KjEpr2BaMOxJS5LiA6fs/CNqVTF/sTTgZAv7Dm+MH0yNuKm4kpPvcLaVeBpVE09bnyCHrNKzZes+Fw=="],
"@zag-js/number-input": ["@zag-js/number-input@1.35.3", "", { "dependencies": { "@internationalized/number": "3.6.5", "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-uqawVybAcLcefVEHMVONuAA5kDSDPP5TsROr5PnAyFlhM1iD85+r3KAfCueoDX5w2X4ibbu9o2tdV6zTFKD/nQ=="],
"@zag-js/pagination": ["@zag-js/pagination@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-fKm4s5KAd12RiCI/EDmmGKjPQ+i2qS/UsJPdMe65yb/4mY5OibwV2zyHcVeFsOD4gBZpnU6kYlDAGSttmLWLlQ=="],
"@zag-js/password-input": ["@zag-js/password-input@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-etd0gm6ELAm3y+cFhPU+TYm8khm9cL5Mg5m2DcZxu1Mqpj7JY0LsXZ8SFOdCZgTIHuMEhKBiYfnuyMAd4CJztA=="],
"@zag-js/pin-input": ["@zag-js/pin-input@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-ZFt+WIHMdVlSg29BrQLFq5ijabiUO3tXMhoKhjjzTSe/tLqfNeu3UxFB6y/FYpn8+Cvn6xwvhu3lgnORYmI0zQ=="],
"@zag-js/popover": ["@zag-js/popover@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/aria-hidden": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-trap": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/remove-scroll": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-+MIEENPsbKPxzoNuDI/C5d5ZN9uxnfZ+MBDc5C5XSgjjg9FcvMXClNq7IFM1aZi24peRXg9cMNf//lApVRT37w=="],
"@zag-js/popper": ["@zag-js/popper@1.35.3", "", { "dependencies": { "@floating-ui/dom": "^1.7.5", "@zag-js/dom-query": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-gpB7Xn9WtlfrUsIVbSgNQGDwgNOL/cSGt0Id3wEQKArmqVC704EWtPvXzOMMybBEdm8YW2hQrXuo+o66abI1Sg=="],
"@zag-js/presence": ["@zag-js/presence@1.35.3", "", { "dependencies": { "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3" } }, "sha512-ev5E7+U9IZAGvEaflpdVLHaZl8ZaQMhGB3ypd0yKhPwXeM51obV8w3+5HjzTqHPl8TKuoHWL31YaiUBd5EuS6w=="],
"@zag-js/progress": ["@zag-js/progress@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-u0GxQN1AfXMAgzYOUMxKQA12DyuAP0svh2S//KvOorTSv7d5hAa8nZXi2cEv5abYsyfKJ6/bc1Z56byzW1jVZw=="],
"@zag-js/qr-code": ["@zag-js/qr-code@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3", "proxy-memoize": "3.0.1", "uqr": "0.1.2" } }, "sha512-t0Ehwogr49vTNtWyNdQU2tYex7uJyfAn7N/5LgD7FXw8aa+RBMWZWlqjCUvHqJ929tVMrn+LIrQnZCcwNunalA=="],
"@zag-js/radio-group": ["@zag-js/radio-group@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-visible": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-kOzocjqWk3dXuRfyfsHwfw63Z99NHbc7rvVUutSsfXANXi+DFYZHuqdPUwMt+29LfaL15XTOfuGV+yUXDCgQHQ=="],
"@zag-js/rating-group": ["@zag-js/rating-group@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-BmhJZdbaTnd3nFWMY+nR+HF952UhWXfaXXxiBWptSLMBfAYImQTWBMrLgTHCSnVfmFATj4Gb7xQe79FQU8T5fA=="],
"@zag-js/rect-utils": ["@zag-js/rect-utils@1.35.3", "", {}, "sha512-mt/oD3RXdyaX6ZPSd8BO13vvPBJ7QpVWieubE3O0WM3OPhU7ykDMRp/tR7cYMQrzUm04GlY9pbkmSSw2uABxlA=="],
"@zag-js/remove-scroll": ["@zag-js/remove-scroll@1.35.3", "", { "dependencies": { "@zag-js/dom-query": "1.35.3" } }, "sha512-e59z9SbEpPiw0qwNQa2cB5/h30ZCLREaHsCw1TKTANFhwg7v85k9Lq1H/G/49li1CAjmiaOU9BNGlDvbzpNETQ=="],
"@zag-js/scroll-area": ["@zag-js/scroll-area@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-IQwdUws/AckRIHK1z/wHdHurnOeGd8h8Dmspfh3VT7NkwTnxeJ4SW9di9smuD+d25eXkJRuX5zGEDHAyx2IaPQ=="],
"@zag-js/scroll-snap": ["@zag-js/scroll-snap@1.35.3", "", { "dependencies": { "@zag-js/dom-query": "1.35.3" } }, "sha512-NVa2yRm2DQnF6hTV9k7Xz7l8YCZBagZTiqSwNvWKUulKD1csjt2fpBxvUt2cK+1iQnLOey2ydhs7MMsAnXPbJA=="],
"@zag-js/select": ["@zag-js/select@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/collection": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-visible": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-ztszGHWvlbBDE0YT5LYPH+sMd6VH1ct5pH/M9VSzIUO6C5PARkW0NwSVQ1rCQJMj4sfvSE1gC1/r7urRzqEcUQ=="],
"@zag-js/signature-pad": ["@zag-js/signature-pad@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3", "perfect-freehand": "^1.2.3" } }, "sha512-jvtxxzAQ8fre11zWUh6HflG4Ycr5z83Wba4pONRJbUE/vNgkJQ7yJgfyUl1QTlkn8Arfg2Zwoxu9GIq80HLZWg=="],
"@zag-js/slider": ["@zag-js/slider@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-Th142JO4Fqla5AWhGrTW6CQicwvTw87PdVpur/WotQ7brlZIww5HipzEMh5eQJSWfwpKD4PI2bYK9V/ZE/mpXA=="],
"@zag-js/solid": ["@zag-js/solid@1.35.3", "", { "dependencies": { "@solid-primitives/keyed": "^1.5.3", "@zag-js/core": "1.35.3", "@zag-js/store": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" }, "peerDependencies": { "solid-js": ">=1.1.3" } }, "sha512-Gp6jFWwLeToD+Y0lcEwi15XIHSmJbyPhl/klxKQWWpr7+p3OAoNWwOQbxkhRU7M9hhJrYYsENfSETwG9QhMfBQ=="],
"@zag-js/splitter": ["@zag-js/splitter@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-IsIbRwzjr5amGANEDsZDSToaSn8wHUWvS2l0XHmf3BiiguVApaZgQTlfqthVQC9hBHMOaGIXIW1CFUOrQYkvUQ=="],
"@zag-js/steps": ["@zag-js/steps@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-TYIrqV+v9/ULhvrTRBtQFFvJQPPTWOmjFXxlIxDwozek5R4dCIyeUYt1/ChJEc2mNETocbfDVSTxRO1dwCFpwQ=="],
"@zag-js/store": ["@zag-js/store@1.35.3", "", { "dependencies": { "proxy-compare": "3.0.1" } }, "sha512-7kEV4T/20DU36UIfVMzuDlLhWSSEy/vabmpiB700tcdD9BBBODTiSg3ZeljW17dQbvE545vZOFEjVf/cQ5LVGA=="],
"@zag-js/switch": ["@zag-js/switch@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-visible": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-EP/2cJ46sd+6C5x5+89jn/9NOpM05CRESYB4RMhOnTe/WFtcS4IpiYtVHFhikdXkvJoibm67O2EHep2Pm/Xj4w=="],
"@zag-js/tabs": ["@zag-js/tabs@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-lZKlDmxE25miCikj9QZCCnL02SVV2K14KZy5bn7+XDgrWlfSNTpNTj8r5E3zGlSgio5pkTGou57ASqS7WaPDWg=="],
"@zag-js/tags-input": ["@zag-js/tags-input@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/auto-resize": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/interact-outside": "1.35.3", "@zag-js/live-region": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-HqyoQ3DZFhByOGnDShFfxi6u0bIf7aSVTlwmAvcL+b2ZhyU6/wIMGc4WJE7BMx1NYWM/jNLHedvGExAI8R0kXQ=="],
"@zag-js/timer": ["@zag-js/timer@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-edmgitbRgsq+msxvVB4wc17Q5d5k63zMWaLJnWjUdDGAgEtM6/HNxwGb3riv46S2U3RgYxaaHTNZ/M7EE5mvYw=="],
"@zag-js/toast": ["@zag-js/toast@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-whlR791GHdnMD21nNPsl2Dbql8+qu1wBZl75QzwYrjR8FlKjp8bhr3gXKzQEddcBXe9GPEFGvUs4iCyXsuTbpg=="],
"@zag-js/toggle": ["@zag-js/toggle@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-aFfHKuR4sKzglhkmWLA+0RTNPs9dfeqwtc96qljawGYfAYWJXkEPYK9dFfVa+arZ7L84xBi24QSLiTg7LGSFLw=="],
"@zag-js/toggle-group": ["@zag-js/toggle-group@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-Gn6JHzkQ4tlttjZcE0ZjIdxYkFeVp9VHrcMVizjJTkGZRmQ+kPZ5G/wOsZhIrvLX3Dw6Y0NkuBcP+jDHz/o3TA=="],
"@zag-js/tooltip": ["@zag-js/tooltip@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-visible": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-/pImDGYl79MfLdvEphj3rSvNdj2tLW4GwGEncgdLM/GKwQiEUjfi/9EJOfLYP23M4lOOnoW7orehJ9xeaXOAkA=="],
"@zag-js/tour": ["@zag-js/tour@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dismissable": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/focus-trap": "1.35.3", "@zag-js/interact-outside": "1.35.3", "@zag-js/popper": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-DI2aCXmZaE9KcPZDs9itc2BO7ixLApJ/yVRfM69pXwVOrucdSeDDNPFkfbhj5XwB+9VjjZEkqWFHKntRIyPl5g=="],
"@zag-js/tree-view": ["@zag-js/tree-view@1.35.3", "", { "dependencies": { "@zag-js/anatomy": "1.35.3", "@zag-js/collection": "1.35.3", "@zag-js/core": "1.35.3", "@zag-js/dom-query": "1.35.3", "@zag-js/types": "1.35.3", "@zag-js/utils": "1.35.3" } }, "sha512-DbHaLxSNa1goE3o3IsXxEdzp8P5dvmkk1rVWgNUUIhpA+44idEjSSNXJkHPl18Mk5blqSMVjK1EX91oqai01Vw=="],
"@zag-js/types": ["@zag-js/types@1.35.3", "", { "dependencies": { "csstype": "3.2.3" } }, "sha512-Fnm3AMs1lfb55hlkip/eJeWHOjFB3gSi1JkZlkkdltG2l7y/zsHkumPSe6jIKy+DRRIFKRCyXVTatbPN27bO3w=="],
"@zag-js/utils": ["@zag-js/utils@1.35.3", "", {}, "sha512-LHcC+9y6TFhDsIz9I3koYxONl2JFfx5yQDzc6ZEQO2cqzXedRcN0R9IPqNGCX7JuhGt14ctDkVCm1JWGP2J6Wg=="],
"@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="],
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
@ -721,9 +587,7 @@
"ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
"babel-plugin-jsx-dom-expressions": ["babel-plugin-jsx-dom-expressions@0.40.5", "", { "dependencies": { "@babel/helper-module-imports": "7.18.6", "@babel/plugin-syntax-jsx": "^7.18.6", "@babel/types": "^7.20.7", "html-entities": "2.3.3", "parse5": "^7.1.2" }, "peerDependencies": { "@babel/core": "^7.20.12" } }, "sha512-8TFKemVLDYezqqv4mWz+PhRrkryTzivTGu0twyLrOkVZ0P63COx2Y04eVsUjFlwSOXui1z3P3Pn209dokWnirg=="],
"babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
"balanced-match": ["balanced-match@4.0.3", "", {}, "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g=="],
@ -741,12 +605,12 @@
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"cmdk-solid": ["cmdk-solid@1.1.2", "", { "dependencies": { "@kobalte/core": "^0.12.4", "@kobalte/utils": "^0.9.0", "@solid-primitives/deep": "^0.2.7", "@solid-primitives/mutation-observer": "^1.1.17" }, "peerDependencies": { "solid-js": "^1.8.0" } }, "sha512-E0g6bwr2Z92Ib1K2WHwGsq6xsrkVVSN1oe5YTg8LCV3iIwtyZORwmR+8n0xrecqycdhgAar8vWUjtB1OJt9tiA=="],
"comlink": ["comlink@4.4.2", "", {}, "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g=="],
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
"cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="],
"crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
@ -761,22 +625,16 @@
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
"electrobun": ["electrobun@1.15.1", "", { "dependencies": { "@babylonjs/core": "^7.45.0", "@types/bun": "^1.3.8", "png-to-ico": "^2.1.8", "rcedit": "^4.0.1", "three": "^0.165.0" }, "bin": { "electrobun": "bin/electrobun.cjs" } }, "sha512-FJ7XaTzbCPuH2ABK615WJyrfacoMdYR33tHJ0j/2JIw2srK4lHN9iSjPpvRVL1UveIQB7zRQ2pmVjeI4evwJ+g=="],
"electrobun-hello-world": ["electrobun-hello-world@workspace:packages/desktop"],
"electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="],
"embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
"embla-carousel-reactive-utils": ["embla-carousel-reactive-utils@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A=="],
"embla-carousel-solid": ["embla-carousel-solid@8.6.0", "", { "dependencies": { "embla-carousel": "8.6.0", "embla-carousel-reactive-utils": "8.6.0" }, "peerDependencies": { "solid-js": "^1.0.0" } }, "sha512-xQQjPZL+CQ4n2KemoeTu0BD9mk8tkVuSMOe/GEnzKM0lAoXY/akSYBNZ4dvJDE7TMeUCAuI6D9742TTLglGq/A=="],
"enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="],
"entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
"esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
@ -785,7 +643,7 @@
"eslint": ["eslint@10.0.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.3", "@eslint/config-helpers": "^0.5.2", "@eslint/core": "^1.1.1", "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.1.1", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ=="],
"eslint-plugin-solid": ["eslint-plugin-solid@0.14.5", "", { "dependencies": { "@typescript-eslint/utils": "^7.13.1 || ^8.0.0", "estraverse": "^5.3.0", "is-html": "^2.0.0", "kebab-case": "^1.0.2", "known-css-properties": "^0.30.0", "style-to-object": "^1.0.6" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "typescript": ">=4.8.4" } }, "sha512-nfuYK09ah5aJG/oEN6P1qziy1zLgW4PDWe75VNPi4CEFYk1x2AEqwFeQfEPR7gNn0F2jOeqKhx2E+5oNCOBYWQ=="],
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
"eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="],
@ -821,6 +679,8 @@
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
"globals": ["globals@17.4.0", "", {}, "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw=="],
@ -829,28 +689,20 @@
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"html-entities": ["html-entities@2.3.3", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="],
"html-tags": ["html-tags@3.3.1", "", {}, "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ=="],
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
"is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-html": ["is-html@2.0.0", "", { "dependencies": { "html-tags": "^3.0.0" } }, "sha512-S+OpgB5i7wzIue/YSE5hg0e5ZYfG3hhpNh9KGl6ayJ38p7ED6wxQLd1TV91xHpcTvw90KMJ9EwN3F/iNflHBVg=="],
"is-what": ["is-what@4.1.16", "", {}, "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="],
"is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
"isbot": ["isbot@5.1.36", "", {}, "sha512-C/ZtXyJqDPZ7G7JPr06ApWyYoHjYexQbS6hPYD4WYCzpv2Qes6Z+CCEfTX4Owzf+1EJ933PoI2p+B9v7wpGZBQ=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
@ -867,12 +719,8 @@
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
"kebab-case": ["kebab-case@1.0.2", "", {}, "sha512-7n6wXq4gNgBELfDCpzKc+mRrZFs7D+wgfF5WRFLNAr4DA/qtr9Js8uOAVAfHhuLMfAcQ0pRKqbpjx+TcJVdE1Q=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"known-css-properties": ["known-css-properties@0.30.0", "", {}, "sha512-VSWXYUnsPu9+WYKkfmJyLKtIvaRJi1kXUqVmBACORXZQxT5oZDsoZ2vQP+bQFDnWtpI/4eq3MLoRMjI2fnLzTQ=="],
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
"lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="],
@ -903,12 +751,10 @@
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
"lucide-solid": ["lucide-solid@0.564.0", "", { "peerDependencies": { "solid-js": "^1.4.7" } }, "sha512-gZdibdRlnooRdKek2zhNGnep/N5TSlOSMWgUXiVtglys728zLLRupyaI7IfZ3USS/p6MG5eu2RKM2CARRYiXVg=="],
"lucide-react": ["lucide-react@0.564.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-JJ8GVTQqFwuliifD48U6+h7DXEHdkhJ/E87kksGByII3qHxtPciVb8T8woQONHBQgHVOl7rSMrrip3SeVNy7Fg=="],
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"merge-anything": ["merge-anything@5.1.7", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ=="],
"minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
@ -927,14 +773,10 @@
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"perfect-freehand": ["perfect-freehand@1.2.3", "", {}, "sha512-bHZSfqDHGNlPpgH2yxXgPHlQSPpEbo+qg7li0M78J9vNAi2yjwLeA4x79BEQhX44lEWpCLSFCeRZwpw0niiXPA=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
@ -949,16 +791,26 @@
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
"proxy-compare": ["proxy-compare@3.0.1", "", {}, "sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q=="],
"proxy-memoize": ["proxy-memoize@3.0.1", "", { "dependencies": { "proxy-compare": "^3.0.0" } }, "sha512-VDdG/VYtOgdGkWJx7y0o7p+zArSf2383Isci8C+BP3YXgMYDoPd3cCBjw0JdWb6YBb9sFiOPbAADDVTPJnh+9g=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"rcedit": ["rcedit@4.0.1", "", { "dependencies": { "cross-spawn-windows-exe": "^1.1.0" } }, "sha512-bZdaQi34krFWhrDn+O53ccBDw0MkAT2Vhu75SqhtvhQu4OPyFM4RoVheyYiVQYdjhUi6EJMVWQ0tR6bCIYVkUg=="],
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
"react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
"react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
"react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
"rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="],
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"seroval": ["seroval@1.5.0", "", {}, "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw=="],
@ -969,28 +821,12 @@
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"solid-dismissible": ["solid-dismissible@0.1.1", "", { "dependencies": { "@corvu/utils": "~0.4.1" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-9kcKBJIMdS+586cA1g63HYWxKh3h89leeNHbPZ1csYjuni+NvPBtNr11l0iEX2AKKEt6FHk6qNhc/gjoYAW1pA=="],
"solid-focus-trap": ["solid-focus-trap@0.1.9", "", { "dependencies": { "@corvu/utils": "~0.4.2" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-LTyNki6GUJPRLXV5uMWPkYClB07SUMubbr2EkAddiR0CJCF/I283txilMU9RURSr/P8EewMfXWu2o3aWrK7A5A=="],
"solid-js": ["solid-js@1.9.11", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", "seroval-plugins": "~1.5.0" } }, "sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q=="],
"solid-presence": ["solid-presence@0.1.8", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-pWGtXUFWYYUZNbg5YpG5vkQJyOtzn2KXhxYaMx/4I+lylTLYkITOLevaCwMRN+liCVk0pqB6EayLWojNqBFECA=="],
"solid-prevent-scroll": ["solid-prevent-scroll@0.1.10", "", { "dependencies": { "@corvu/utils": "~0.4.1" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-KplGPX2GHiWJLZ6AXYRql4M127PdYzfwvLJJXMkO+CMb8Np4VxqDAg5S8jLdwlEuBis/ia9DKw2M8dFx5u8Mhw=="],
"solid-refresh": ["solid-refresh@0.6.3", "", { "dependencies": { "@babel/generator": "^7.23.6", "@babel/helper-module-imports": "^7.22.15", "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" } }, "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA=="],
"solid-sonner": ["solid-sonner@0.2.8", "", { "peerDependencies": { "solid-js": "^1.6.0" } }, "sha512-EQ2EIznvHHpAmkYh2CTu0AdCgmPJRJWLGFRWygE8j+vMEfvIV2wotHU5qgWzqzVTG1SODGsay2Lwq6ENWx/rPA=="],
"solid-transition-size": ["solid-transition-size@0.1.4", "", { "dependencies": { "@corvu/utils": "~0.3.2" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-ocHVnbfy23CgfaH4cEUR/AFg0Y3CEL8Oh3n9Qv8OHFJgPh+zkmERKZQfi/xH5XvxDCizg8VjPrVUhiHB1Gza8g=="],
"sonner": ["sonner@1.7.4", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="],
"style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="],
"tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="],
"tailwind-scrollbar-hide": ["tailwind-scrollbar-hide@4.0.0", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || >= 4.0.0 || >= 4.0.0-beta.8 || >= 4.0.0-alpha.20" } }, "sha512-gobtvVcThB2Dxhy0EeYSS1RKQJ5baDFkamkhwBvzvevwX6L4XQfpZ3me9s25Ss1ecFVT5jPYJ50n+7xTBJG9WQ=="],
@ -1001,6 +837,10 @@
"three": ["three@0.165.0", "", {}, "sha512-cc96IlVYGydeceu0e5xq70H8/yoVT/tXBxV/W8A/U6uOq7DXc4/s1Mkmnu6SqoYGhSRWWYFOhVwvq6V0VtbplA=="],
"tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
"tiny-warning": ["tiny-warning@1.0.3", "", {}, "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="],
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="],
@ -1021,20 +861,18 @@
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
"uqr": ["uqr@0.1.2", "", {}, "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
"use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
"vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
"vite-plugin-lucide-preprocess": ["vite-plugin-lucide-preprocess@1.4.8", "", { "dependencies": { "magic-string": "^0.30.0" }, "peerDependencies": { "rolldown": ">=0.1", "rollup": ">=1", "vite": ">=2" }, "optionalPeers": ["rolldown", "rollup"] }, "sha512-wIQ1fchys+WD/brwNjJTpy+GoHysPRDI7PFl4Qb6WNleifeC0fHGISMoyXdCjlcbx9wMMWOCPsZOygtk09mMfw=="],
"vite-plugin-solid": ["vite-plugin-solid@2.11.10", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-Yr1dQybmtDtDAHkii6hXuc1oVH9CPcS/Zb2jN/P36qqcrkNnVPsMTzQ06jyzFPFjj3U1IYKMVt/9ZqcwGCEbjw=="],
"vite-tsconfig-paths": ["vite-tsconfig-paths@6.1.1", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" } }, "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg=="],
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
"w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
@ -1051,8 +889,6 @@
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"@corvu/dialog/solid-presence": ["solid-presence@0.2.0", "", { "dependencies": { "@corvu/utils": "~0.4.2" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-YM92o+jvpzX3XGaD4rLYmq/Kc2ZVh47GSCLEufHBFQQIurvZTs8SoGJxO8BJGNDxBKdcS8F3dYhW1SDXp4BNjA=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@eslint/config-helpers/@eslint/core": ["@eslint/core@1.1.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw=="],
@ -1069,48 +905,12 @@
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@tensamin/notifications/@tensamin/ttp": ["TTP@github:Tensamin/TTP#42bee9c", {}, "Tensamin-TTP-42bee9c"],
"@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ=="],
"@tensamin/notifications/@tensamin/ttp": ["TTP@github:Tensamin/TTP#d5edc69", {}, "Tensamin-TTP-d5edc69"],
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@typescript-eslint/type-utils/@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ=="],
"@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.0", "", { "dependencies": { "@typescript-eslint/types": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0" } }, "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w=="],
"@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.56.0", "", {}, "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.0", "@typescript-eslint/tsconfig-utils": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q=="],
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
"bun-types/@types/node": ["@types/node@25.3.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="],
"cmdk-solid/@kobalte/core": ["@kobalte/core@0.12.6", "", { "dependencies": { "@floating-ui/dom": "^1.5.1", "@internationalized/date": "^3.4.0", "@internationalized/number": "^3.2.1", "@kobalte/utils": "^0.9.0", "solid-prevent-scroll": "^0.1.4" }, "peerDependencies": { "solid-js": "^1.8.15" } }, "sha512-+Ta2o2wEqZ2fCqLMkvjT40VHNmcFKdGe8TNDVQbbMPk66qoU6g/DDRFR/Ib7eAjb+C95VoIyk6zaafos2VOo0w=="],
"png-to-ico/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="],
"solid-transition-size/@corvu/utils": ["@corvu/utils@0.3.2", "", { "dependencies": { "@floating-ui/dom": "^1.6.7" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-ZWlyWEE8qV9+CB9OAyo2bTrZGXQN9ZeM+JfYv89zoR+lRACKTDuoOZEdiyL8Uc7U5dUSH1uTqKhTTnaHWb+wZA=="],
"typescript-eslint/@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ=="],
"@typescript-eslint/utils/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.0", "", { "dependencies": { "@typescript-eslint/types": "8.56.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.56.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.0", "@typescript-eslint/types": "^8.56.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.56.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.0", "", { "dependencies": { "@typescript-eslint/types": "8.56.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"@typescript-eslint/utils/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.0", "", {}, "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.0", "", {}, "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
}
}

View file

@ -1,7 +1,7 @@
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import solid from "eslint-plugin-solid/configs/typescript";
import reactHooks from "eslint-plugin-react-hooks";
import * as tsParser from "@typescript-eslint/parser";
export default [
@ -12,7 +12,6 @@ export default [
...tseslint.configs.recommended,
{
files: ["**/*.{ts,tsx}"],
...solid,
languageOptions: {
parser: tsParser,
globals: {
@ -20,5 +19,11 @@ export default [
...globals.node,
},
},
plugins: {
"react-hooks": reactHooks,
},
rules: {
...reactHooks.configs.recommended.rules,
},
},
];

View file

@ -16,9 +16,11 @@
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/node": "^25.4.0",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@typescript-eslint/parser": "^8.57.0",
"eslint": "^10.0.3",
"eslint-plugin-solid": "^0.14.5",
"eslint-plugin-react-hooks": "^5.2.0",
"globals": "^17.4.0",
"prettier": "^3.8.1",
"typescript": "^5.9.3",

View file

@ -15,9 +15,9 @@
"build": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"@solidjs/router": "^0.15.4",
"@tanstack/solid-query": "^5.90.23",
"@tanstack/solid-virtual": "^3.13.21",
"@tanstack/react-query": "^5.0.0",
"@tanstack/react-router": "^1.0.0",
"@tanstack/react-virtual": "^3.0.0",
"@tensamin/core-crypto": "workspace:*",
"@tensamin/ttp": "workspace:*",
"@tensamin/core-storage": "workspace:*",
@ -25,9 +25,9 @@
"@tensamin/markdown": "workspace:*",
"@tensamin/shared": "workspace:*",
"@tensamin/ui": "workspace:*",
"lucide-solid": "^0.564.0",
"solid-js": "^1.9.11",
"solid-sonner": "^0.2.8",
"lucide-react": "^0.564.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"zod": "^4.3.6"
}
}

View file

@ -1,13 +1,13 @@
import { useSocket } from "@tensamin/ttp/context";
import type { BoundSendFn } from "@tensamin/ttp/core";
import type { Socket } from "@tensamin/shared/data";
import type { RawMessages } from "../values";
export async function getMessages(
send: BoundSendFn<Socket>,
amount: number,
offset: number,
user_id: number,
): Promise<RawMessages> {
const { send } = useSocket();
const messages = await send("messages_get", {
amount: amount,
offset: offset,

View file

@ -1,35 +1,35 @@
import Input from "@tensamin/markdown/input";
import { Card, CardHeader } from "@tensamin/ui/card";
import { useStorage } from "@tensamin/core-storage/context";
import { createEffect, createSignal } from "solid-js";
import * as React from "react";
import { Button } from "@tensamin/ui/button";
import { Plus, Laugh, Clapperboard } from "lucide-solid";
import { Plus, Laugh, Clapperboard } from "lucide-react";
import { useChat } from "../context";
import { useSocket } from "@tensamin/ttp/context";
import { useCrypto } from "@tensamin/core-crypto/context";
import { log, toast } from "@tensamin/shared/log";
export default function InputComponent() {
const [value, setValue] = createSignal("");
const [invertEnterBehavior, setInvertEnterBehavior] = createSignal(false);
const [value, setValue] = React.useState("");
const [invertEnterBehavior, setInvertEnterBehavior] = React.useState(false);
const { encrypt } = useCrypto();
const { send } = useSocket();
const { addLiveMessage, sharedSecret, userId } = useChat();
const { load } = useStorage();
createEffect(() => {
React.useEffect(() => {
void load("chat_invert_enter_behaviour").then((shouldInvert) => {
setInvertEnterBehavior(shouldInvert);
});
});
}, [load]);
async function handleSubmit() {
if (value().trim() === "") return;
if (value.trim() === "") return;
const time = Date.now();
const currentValue = value();
const currentValue = value;
const currentUserId = userId();
addLiveMessage({
@ -61,36 +61,36 @@ export default function InputComponent() {
}
return (
<Card class="rounded-none rounded-t-xl border border-input/50 border-b-0">
<CardHeader class="p-0 flex flex-col">
<Card className="rounded-none rounded-t-xl border border-input/50 border-b-0">
<CardHeader className="p-0 flex flex-col">
<Input
placeholder="Send a message..."
value={value()}
value={value}
setValue={setValue}
onSubmit={handleSubmit}
invertEnterBehavior={invertEnterBehavior()}
invertEnterBehavior={invertEnterBehavior}
/>
<div class="w-full flex justify-between gap-2 p-2 pt-0">
<div class="flex gap-2">
<div className="w-full flex justify-between gap-2 p-2 pt-0">
<div className="flex gap-2">
<Button
class="w-9 h-9 p-0 border border-ring/10"
className="w-9 h-9 p-0 border border-ring/10"
variant="secondary"
>
<Plus size={20} />
</Button>
<div class="w-auto flex">
<div className="w-auto flex">
<p>Files list</p>
</div>
</div>
<div class="flex gap-2">
<div className="flex gap-2">
<Button
class="w-9 h-9 p-0 border border-ring/10"
className="w-9 h-9 p-0 border border-ring/10"
variant="secondary"
>
<Laugh size={20} />
</Button>
<Button
class="w-9 h-9 p-0 border border-ring/10"
className="w-9 h-9 p-0 border border-ring/10"
variant="secondary"
>
<Clapperboard size={20} />

View file

@ -1,5 +1,5 @@
import { useCrypto } from "@tensamin/core-crypto/context";
import { createEffect, createSignal, onCleanup, Show } from "solid-js";
import * as React from "react";
import { useChat } from "../context";
import type { RawMessage } from "../values";
import { log } from "@tensamin/shared/log";
@ -12,18 +12,17 @@ export default function Message(props: {
const { sharedSecret } = useChat();
const { decrypt } = useCrypto();
const message = () => props.message;
const [decodedContent, setDecodedContent] = createSignal("");
const [isReady, setIsReady] = createSignal(false);
const [decodedContent, setDecodedContent] = React.useState("");
const [isReady, setIsReady] = React.useState(false);
createEffect(() => {
React.useEffect(() => {
if (props.notEncrypted) {
setDecodedContent(message().content);
setDecodedContent(props.message.content);
setIsReady(true);
return;
}
const content = message().content;
const content = props.message.content;
const secret = sharedSecret();
let active = true;
@ -57,23 +56,21 @@ export default function Message(props: {
setIsReady(true);
});
onCleanup(() => {
return () => {
active = false;
});
});
};
}, [decrypt, props.message.content, props.notEncrypted, sharedSecret]);
return (
<div class="w-full flex justify-start">
<div className="w-full flex justify-start">
<div
class={`animate-in fade-in duration-200 max-w-[80%] rounded-xl px-2 py-1 whitespace-pre-wrap wrap-break-word ${
message().sent_by_self
className={`animate-in fade-in duration-200 max-w-[80%] rounded-xl px-2 py-1 whitespace-pre-wrap wrap-break-word ${
props.message.sent_by_self
? "bg-primary text-primary-foreground"
: "bg-muted"
}`}
>
<Show when={isReady()}>
<Text value={decodedContent()} />
</Show>
{isReady ? <Text value={decodedContent} /> : null}
</div>
</div>
);

View file

@ -1,49 +1,47 @@
import {
createContext,
createEffect,
createSignal,
useContext,
} from "solid-js";
import type { ParentProps } from "solid-js";
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query";
import * as React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useRouterState } from "@tanstack/react-router";
import type { RawMessage, RawMessages } from "./values";
import { useSearchParams } from "@solidjs/router";
import { useCrypto } from "@tensamin/core-crypto/context";
import { useUser } from "@tensamin/core-user/context";
import { useStorage } from "@tensamin/core-storage/context";
import { useSocket } from "@tensamin/ttp/context";
export const context = createContext<contextType>();
export const context = React.createContext<contextType | undefined>(undefined);
const queryClient = new QueryClient();
export default function Provider(
props: ParentProps & {
getMessages: (
amount: number,
offset: number,
user_id: number,
) => Promise<RawMessages>;
},
props: { children: React.ReactNode },
) {
const { get_shared_secret } = useCrypto();
const { get } = useUser();
const { load } = useStorage();
const { send } = useSocket();
const [liveMessages, setLiveMessages] = createSignal<RawMessages>([]);
const [currentSharedSecret, setCurrentSharedSecret] = createSignal("");
const [liveMessagesState, setLiveMessagesState] = React.useState<RawMessages>([]);
const [currentSharedSecret, setCurrentSharedSecret] = React.useState("");
const [searchParams] = useSearchParams();
const userId = () => Number(searchParams.id ?? 0);
const locationSearch = useRouterState({
select: (state) => state.location.search,
});
createEffect(() => {
const recipientId = userId();
const userIdValue = React.useMemo(() => {
const rawId = (locationSearch as unknown as { id?: unknown })?.id;
return Number(rawId ?? 0);
}, [locationSearch]);
React.useEffect(() => {
const recipientId = userIdValue;
if (!recipientId) {
setCurrentSharedSecret("");
return;
}
get(recipientId).then(async (recipientData) => {
let active = true;
void get(recipientId).then(async (recipientData) => {
const ownId = await load("user_id");
const privateKey = await load("private_key");
const ownData = await get(ownId);
@ -53,39 +51,58 @@ export default function Provider(
recipientData.public_key,
);
if (active) {
setCurrentSharedSecret(sharedSecret);
});
}
});
async function customGetMessages(amount: number, offset: number) {
const messages = await props.getMessages(amount, offset, userId());
const sorted = [...messages].sort((a, b) => a.timestamp - b.timestamp);
return () => {
active = false;
};
}, [get, get_shared_secret, load, userIdValue]);
const customGetMessages = React.useCallback(
async (amount: number, offset: number) => {
const messages = await send("messages_get", {
amount,
offset,
user_id: userIdValue,
});
if (messages.type.startsWith("error")) {
throw new Error(messages.type);
}
const rawMessages = messages.data.messages;
const sorted = [...rawMessages].sort((a, b) => a.timestamp - b.timestamp);
return sorted;
}
},
[send, userIdValue],
);
function addLiveMessage(message: RawMessage) {
setLiveMessages((prev) => [...prev, message]);
}
const addLiveMessage = React.useCallback((message: RawMessage) => {
setLiveMessagesState((prev) => [...prev, message]);
}, []);
function clearLiveMessages() {
setLiveMessages([]);
}
const clearLiveMessages = React.useCallback(() => {
setLiveMessagesState([]);
}, []);
const value = React.useMemo<contextType>(
() => ({
getMessages: customGetMessages,
liveMessages: () => liveMessagesState,
addLiveMessage,
clearLiveMessages,
sharedSecret: () => currentSharedSecret,
userId: () => userIdValue,
}),
[addLiveMessage, clearLiveMessages, currentSharedSecret, customGetMessages, liveMessagesState, userIdValue],
);
return (
<QueryClientProvider client={queryClient}>
<context.Provider
value={{
getMessages: customGetMessages,
liveMessages,
addLiveMessage,
clearLiveMessages,
sharedSecret: currentSharedSecret,
userId,
}}
>
{props.children}
</context.Provider>
<context.Provider value={value}>{props.children}</context.Provider>
</QueryClientProvider>
);
}
@ -100,7 +117,7 @@ type contextType = {
};
export function useChat(): contextType {
const ctx = useContext(context);
const ctx = React.useContext(context);
if (!ctx) {
throw new Error("useChat must be used within a ChatProvider");
}

View file

@ -1,7 +1,7 @@
import { createVirtualizer } from "@tanstack/solid-virtual";
import { useInfiniteQuery } from "@tanstack/solid-query";
import * as React from "react";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useVirtualizer } from "@tanstack/react-virtual";
import { useChat } from "./context";
import { createEffect, createMemo, createSignal, For } from "solid-js";
import InputComponent from "./components/input";
import Message from "./components/message";
@ -11,21 +11,22 @@ const PAGE_SIZE = 50;
export default function Screen() {
const { getMessages, liveMessages, clearLiveMessages, userId } = useChat();
// eslint-disable-next-line no-unassigned-vars
let scrollRef!: HTMLDivElement;
const scrollRef = React.useRef<HTMLDivElement | null>(null);
const [hasScrolledToBottomInitially, setHasScrolledToBottomInitially] =
createSignal(false);
const [lastLiveMessageCount, setLastLiveMessageCount] = createSignal(0);
const [prependAnchor, setPrependAnchor] = createSignal<{
React.useState(false);
const [lastLiveMessageCount, setLastLiveMessageCount] = React.useState(0);
const [prependAnchor, setPrependAnchor] = React.useState<{
totalSize: number;
scrollTop: number;
} | null>(null);
const messagesQuery = useInfiniteQuery(() => ({
queryKey: ["chat-messages", String(userId())],
const chatUserId = userId();
const messagesQuery = useInfiniteQuery({
queryKey: ["chat-messages", String(chatUserId)],
initialPageParam: 0,
queryFn: ({ pageParam }) => getMessages(PAGE_SIZE, pageParam),
queryFn: ({ pageParam }) => getMessages(PAGE_SIZE, Number(pageParam)),
getNextPageParam: (lastPage, allPages) => {
if (lastPage.length < PAGE_SIZE) {
return undefined;
@ -33,54 +34,30 @@ export default function Screen() {
return allPages.length * PAGE_SIZE;
},
}));
const historicalMessages = createMemo(() => {
const pages = messagesQuery.data?.pages ?? [];
return [...pages].reverse().flat();
});
createEffect(() => {
userId();
const historicalMessages = React.useMemo(() => {
const pages = messagesQuery.data?.pages ?? [];
return [...pages].reverse().flat();
}, [messagesQuery.data]);
React.useEffect(() => {
clearLiveMessages();
setHasScrolledToBottomInitially(false);
setLastLiveMessageCount(0);
setPrependAnchor(null);
});
}, [chatUserId, clearLiveMessages]);
const messages = createMemo(() => [
...historicalMessages(),
...liveMessages(),
]);
const messages = React.useMemo(
() => [...historicalMessages, ...liveMessages()],
[historicalMessages, liveMessages],
);
async function onScroll() {
if (!scrollRef) {
return;
}
if (scrollRef.scrollTop > 96) {
return;
}
if (messagesQuery.isFetchingNextPage || !messagesQuery.hasNextPage) {
return;
}
setPrependAnchor({
totalSize: virtualizer.getTotalSize(),
scrollTop: scrollRef.scrollTop,
});
await messagesQuery.fetchNextPage();
}
const virtualizer = createVirtualizer({
get count() {
return messages().length;
},
getScrollElement: () => scrollRef,
const virtualizer = useVirtualizer({
count: messages.length,
getScrollElement: () => scrollRef.current,
estimateSize: (index) => {
const message = messages()[index];
const message = messages[index];
if (!message) {
return 56;
@ -102,82 +79,107 @@ export default function Screen() {
overscan: 6,
});
createEffect(() => {
const currentMessages = messages();
const onScroll = React.useCallback(async () => {
if (!scrollRef.current) {
return;
}
if (scrollRef.current.scrollTop > 96) {
return;
}
if (messagesQuery.isFetchingNextPage || !messagesQuery.hasNextPage) {
return;
}
setPrependAnchor({
totalSize: virtualizer.getTotalSize(),
scrollTop: scrollRef.current.scrollTop,
});
await messagesQuery.fetchNextPage();
}, [messagesQuery, virtualizer]);
React.useEffect(() => {
if (
!scrollRef ||
hasScrolledToBottomInitially() ||
currentMessages.length === 0
!scrollRef.current ||
hasScrolledToBottomInitially ||
messages.length === 0
) {
return;
}
queueMicrotask(() => {
if (!scrollRef) {
if (!scrollRef.current) {
return;
}
scrollRef.scrollTop = scrollRef.scrollHeight;
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
setHasScrolledToBottomInitially(true);
});
});
}, [hasScrolledToBottomInitially, messages]);
createEffect(() => {
React.useEffect(() => {
const count = liveMessages().length;
if (!scrollRef) {
if (!scrollRef.current) {
return;
}
if (count > lastLiveMessageCount()) {
if (count > lastLiveMessageCount) {
queueMicrotask(() => {
if (!scrollRef) {
if (!scrollRef.current) {
return;
}
scrollRef.scrollTop = scrollRef.scrollHeight;
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
});
}
setLastLiveMessageCount(count);
});
}, [lastLiveMessageCount, liveMessages]);
createEffect(() => {
const anchor = prependAnchor();
if (!scrollRef || !anchor || messagesQuery.isFetchingNextPage) {
React.useEffect(() => {
if (!scrollRef.current || !prependAnchor || messagesQuery.isFetchingNextPage) {
return;
}
queueMicrotask(() => {
if (!scrollRef) {
if (!scrollRef.current) {
return;
}
const delta = virtualizer.getTotalSize() - anchor.totalSize;
scrollRef.scrollTop = anchor.scrollTop + delta;
const delta = virtualizer.getTotalSize() - prependAnchor.totalSize;
scrollRef.current.scrollTop = prependAnchor.scrollTop + delta;
setPrependAnchor(null);
});
});
}, [messagesQuery.isFetchingNextPage, prependAnchor, virtualizer]);
return (
<div class="w-full h-full flex flex-col overflow-hidden px-2">
<div className="w-full h-full flex flex-col overflow-hidden px-2">
<div
ref={scrollRef}
id="chat_container"
class={`flex-1 max-h-[calc(100vh-151px)] overflow-y-auto px-2.5`}
onScroll={onScroll}
className="flex-1 max-h-[calc(100vh-151px)] overflow-y-auto px-2.5"
onScroll={() => {
void onScroll();
}}
>
<div
class="relative w-full"
className="relative w-full"
style={{
height: `${virtualizer.getTotalSize()}px`,
}}
>
<For each={virtualizer.getVirtualItems()}>
{(virtualRow) => (
{virtualizer.getVirtualItems().map((virtualRow) => {
const message = messages[virtualRow.index];
if (!message) {
return null;
}
return (
<div
key={virtualRow.key}
data-index={virtualRow.index}
ref={virtualizer.measureElement}
style={{
@ -189,13 +191,10 @@ export default function Screen() {
padding: "4px 0",
}}
>
<Message
message={messages()[virtualRow.index]}
notEncrypted={messages()[virtualRow.index].not_encrypted}
/>
<Message message={message} notEncrypted={message.not_encrypted} />
</div>
)}
</For>
);
})}
</div>
</div>
<InputComponent />

View file

@ -3,8 +3,7 @@
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true

View file

@ -16,6 +16,7 @@
"@noble/curves": "^2.0.1",
"@tensamin/ui": "workspace:*",
"comlink": "^4.4.2",
"solid-js": "^1.9.11"
"react": "^19.2.0",
"react-dom": "^19.2.0"
}
}

View file

@ -1,45 +1,41 @@
import {
createContext,
onMount,
onCleanup,
createSignal,
Show,
useContext,
} from "solid-js";
import type { ParentProps } from "solid-js";
import * as React from "react";
import * as Comlink from "comlink";
import Loading from "@tensamin/ui/screens/loading";
export const context = createContext<contextType>();
export const context = React.createContext<contextType | undefined>(undefined);
export default function Provider(props: ParentProps) {
let apiRef: ApiRef | null = null;
const [isWorkerReady, setIsWorkerReady] = createSignal(false);
export default function Provider(props: { children: React.ReactNode }) {
const apiRef = React.useRef<ApiRef | null>(null);
const [isWorkerReady, setIsWorkerReady] = React.useState(false);
const { encrypt, decrypt, get_shared_secret } = createCryptoActions(
() => apiRef,
const { encrypt, decrypt, get_shared_secret } = React.useMemo(
() => createCryptoActions(() => apiRef.current),
[],
);
onMount(() => {
React.useEffect(() => {
const worker = new Worker(new URL("./worker.ts", import.meta.url), {
type: "module",
});
apiRef = Comlink.wrap(worker);
apiRef.current = Comlink.wrap(worker);
setIsWorkerReady(true);
onCleanup(() => {
apiRef = null;
return () => {
apiRef.current = null;
worker.terminate();
});
});
setIsWorkerReady(false);
};
}, []);
if (!isWorkerReady) {
return <Loading progress={10} />;
}
return (
<Show when={isWorkerReady()} fallback={<Loading progress={10} />}>
<context.Provider value={{ encrypt, decrypt, get_shared_secret }}>
{props.children}
</context.Provider>
</Show>
);
}
@ -68,7 +64,7 @@ export function createCryptoActions(
): contextType {
const encrypt = async (secret: string, message: string): Promise<string> => {
const apiRef = getApiRef();
if (!apiRef) throw "API not initialized";
if (!apiRef) throw new Error("API not initialized");
return await apiRef.encrypt(secret, message);
};
@ -77,7 +73,7 @@ export function createCryptoActions(
encryptedMessage: string,
): Promise<string> => {
const apiRef = getApiRef();
if (!apiRef) throw "API not initialized";
if (!apiRef) throw new Error("API not initialized");
return await apiRef.decrypt(secret, encryptedMessage);
};
@ -87,7 +83,7 @@ export function createCryptoActions(
other_public_key: string,
): Promise<string> => {
const apiRef = getApiRef();
if (!apiRef) throw "API not initialized";
if (!apiRef) throw new Error("API not initialized");
return await apiRef.get_shared_secret(
own_private_key,
own_public_key,
@ -99,7 +95,7 @@ export function createCryptoActions(
}
export function useCrypto(): contextType {
const ctx = useContext(context);
const ctx = React.useContext(context);
if (!ctx) {
throw new Error("useCrypto must be used within a CryptoProvider");
}

View file

@ -3,8 +3,7 @@
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true

View file

@ -18,6 +18,7 @@
"@codemirror/state": "^6.5.4",
"@codemirror/view": "^6.39.15",
"@tensamin/ui": "workspace:*",
"solid-js": "^1.9.11"
"react": "^19.2.0",
"react-dom": "^19.2.0"
}
}

View file

@ -21,12 +21,9 @@ import {
indentWithTab,
} from "@codemirror/commands";
import {
createEffect,
createMemo,
onCleanup,
onMount,
type Setter,
} from "solid-js";
useEffect,
useRef,
} from "react";
import { collectInlineRanges, ensureMarkdownStyles } from "./markdown";
@ -34,7 +31,7 @@ export type InputProps = {
ref?: HTMLDivElement;
placeholder?: string;
value: string;
setValue: Setter<string>;
setValue: (value: string) => void;
onSubmit?: () => void;
invertEnterBehavior?: boolean;
};
@ -79,21 +76,18 @@ const markdownDecorations = ViewPlugin.fromClass(
export default function Input(props: InputProps) {
ensureMarkdownStyles();
// eslint-disable-next-line no-unassigned-vars
let element: HTMLDivElement | undefined;
let view: EditorView | undefined;
let ignoreSync = false;
const elementRef = useRef<HTMLDivElement | null>(null);
const viewRef = useRef<EditorView | undefined>(undefined);
const ignoreSyncRef = useRef(false);
const externalValue = createMemo(() => props.value);
onMount(() => {
if (!element) return;
useEffect(() => {
if (!elementRef.current) return;
const state = EditorState.create({
doc: props.value,
extensions: createEditorExtensions(
(value) => {
ignoreSync = true;
ignoreSyncRef.current = true;
props.setValue(value);
},
() => props.placeholder,
@ -102,21 +96,28 @@ export default function Input(props: InputProps) {
),
});
view = new EditorView({
viewRef.current = new EditorView({
state,
parent: element,
});
parent: elementRef.current,
});
createEffect(() => {
const editor = view;
return () => {
viewRef.current?.destroy();
viewRef.current = undefined;
};
// Run once to initialize/destroy the editor instance.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
const editor = viewRef.current;
if (!editor) return;
const next = externalValue();
const next = props.value;
const current = editor.state.doc.toString();
if (ignoreSync) {
ignoreSync = false;
if (ignoreSyncRef.current) {
ignoreSyncRef.current = false;
return;
}
@ -129,14 +130,9 @@ export default function Input(props: InputProps) {
insert: next,
},
});
});
}, [props.value]);
onCleanup(() => {
view?.destroy();
view = undefined;
});
return <div ref={element} class="tm-md-root" />;
return <div ref={elementRef} className="tm-md-root" />;
}
function createEditorExtensions(

View file

@ -1,4 +1,4 @@
import { For, Index, type JSX } from "solid-js";
import * as React from "react";
type InlineNode =
| { type: "text"; value: string }
@ -345,31 +345,53 @@ export function parseMarkdownBlocks(markdown: string): MarkdownBlock[] {
return blocks;
}
export function renderInline(nodes: InlineNode[]): JSX.Element[] {
return nodes.map((node) => {
export function renderInline(nodes: InlineNode[]): React.ReactNode[] {
return nodes.map((node, index) => {
if (node.type === "text") {
return node.value;
}
if (node.type === "strong") {
return <strong class="tm-md-strong">{node.value}</strong>;
return (
<strong key={index} className="tm-md-strong">
{node.value}
</strong>
);
}
if (node.type === "em") {
return <em class="tm-md-em">{node.value}</em>;
return (
<em key={index} className="tm-md-em">
{node.value}
</em>
);
}
if (node.type === "del") {
return <del class="tm-md-del">{node.value}</del>;
return (
<del key={index} className="tm-md-del">
{node.value}
</del>
);
}
if (node.type === "code") {
return <code class="tm-md-code">{node.value}</code>;
return (
<code key={index} className="tm-md-code">
{node.value}
</code>
);
}
if (node.type === "link") {
return (
<a class="tm-md-link" href={node.href} target="_blank" rel="noreferrer">
<a
key={index}
className="tm-md-link"
href={node.href}
target="_blank"
rel="noreferrer"
>
{node.label}
</a>
);
@ -377,7 +399,8 @@ export function renderInline(nodes: InlineNode[]): JSX.Element[] {
return (
<img
class="tm-md-image"
key={index}
className="tm-md-image"
src={node.src}
alt={node.alt}
loading="lazy"
@ -387,44 +410,44 @@ export function renderInline(nodes: InlineNode[]): JSX.Element[] {
});
}
export function renderBlocks(blocks: MarkdownBlock[]): JSX.Element {
export function renderBlocks(blocks: MarkdownBlock[]): React.ReactElement {
return (
<For each={blocks}>
{(block) => {
<>
{blocks.map((block, blockIndex) => {
if (block.type === "heading") {
const className = `tm-md-heading tm-md-h${String(block.level)}`;
if (block.level === 1)
return (
<h1 class={className}>
<h1 key={blockIndex} className={className}>
{renderInline(parseInlineNodes(block.text))}
</h1>
);
if (block.level === 2)
return (
<h2 class={className}>
<h2 key={blockIndex} className={className}>
{renderInline(parseInlineNodes(block.text))}
</h2>
);
if (block.level === 3)
return (
<h3 class={className}>
<h3 key={blockIndex} className={className}>
{renderInline(parseInlineNodes(block.text))}
</h3>
);
if (block.level === 4)
return (
<h4 class={className}>
<h4 key={blockIndex} className={className}>
{renderInline(parseInlineNodes(block.text))}
</h4>
);
if (block.level === 5)
return (
<h5 class={className}>
<h5 key={blockIndex} className={className}>
{renderInline(parseInlineNodes(block.text))}
</h5>
);
return (
<h6 class={className}>
<h6 key={blockIndex} className={className}>
{renderInline(parseInlineNodes(block.text))}
</h6>
);
@ -432,18 +455,18 @@ export function renderBlocks(blocks: MarkdownBlock[]): JSX.Element {
if (block.type === "blockquote") {
return (
<blockquote class="tm-md-blockquote">
<For each={block.text.split("\n")}>
{(line) => <p>{renderInline(parseInlineNodes(line))}</p>}
</For>
<blockquote key={blockIndex} className="tm-md-blockquote">
{block.text.split("\n").map((line, lineIndex) => (
<p key={lineIndex}>{renderInline(parseInlineNodes(line))}</p>
))}
</blockquote>
);
}
if (block.type === "code") {
return (
<pre class="tm-md-pre">
<code class="tm-md-codeblock" data-language={block.language}>
<pre key={blockIndex} className="tm-md-pre">
<code className="tm-md-codeblock" data-language={block.language}>
{block.code}
</code>
</pre>
@ -453,51 +476,43 @@ export function renderBlocks(blocks: MarkdownBlock[]): JSX.Element {
if (block.type === "list") {
const Tag = block.ordered ? "ol" : "ul";
return (
<Tag class={block.ordered ? "tm-md-ol" : "tm-md-ul"}>
<For each={block.items}>
{(item) => (
<li class="tm-md-li">
<Tag key={blockIndex} className={block.ordered ? "tm-md-ol" : "tm-md-ul"}>
{block.items.map((item, itemIndex) => (
<li key={itemIndex} className="tm-md-li">
{item.checked !== null ? (
<input
type="checkbox"
checked={item.checked}
disabled
class="tm-md-checkbox"
className="tm-md-checkbox"
/>
) : null}
<span>{renderInline(parseInlineNodes(item.text))}</span>
</li>
)}
</For>
))}
</Tag>
);
}
if (block.type === "table") {
return (
<div class="tm-md-table-wrap">
<table class="tm-md-table">
<div key={blockIndex} className="tm-md-table-wrap">
<table className="tm-md-table">
<thead>
<tr>
<For each={block.headers}>
{(header) => (
<th>{renderInline(parseInlineNodes(header))}</th>
)}
</For>
{block.headers.map((header, headerIndex) => (
<th key={headerIndex}>{renderInline(parseInlineNodes(header))}</th>
))}
</tr>
</thead>
<tbody>
<For each={block.rows}>
{(row) => (
<tr>
<For each={row}>
{(cell) => (
<td>{renderInline(parseInlineNodes(cell))}</td>
)}
</For>
{block.rows.map((row, rowIndex) => (
<tr key={rowIndex}>
{row.map((cell, cellIndex) => (
<td key={cellIndex}>{renderInline(parseInlineNodes(cell))}</td>
))}
</tr>
)}
</For>
))}
</tbody>
</table>
</div>
@ -505,23 +520,21 @@ export function renderBlocks(blocks: MarkdownBlock[]): JSX.Element {
}
if (block.type === "hr") {
return <hr class="tm-md-hr" />;
return <hr key={blockIndex} className="tm-md-hr" />;
}
return (
<p class="tm-md-p">
<Index each={block.text.split("\n")}>
{(line, idx) => (
<>
{idx > 0 ? <br /> : null}
{renderInline(parseInlineNodes(line()))}
</>
)}
</Index>
<p key={blockIndex} className="tm-md-p">
{block.text.split("\n").map((line, lineIndex) => (
<React.Fragment key={lineIndex}>
{lineIndex > 0 ? <br /> : null}
{renderInline(parseInlineNodes(line))}
</React.Fragment>
))}
</p>
);
}}
</For>
})}
</>
);
}

View file

@ -1,4 +1,4 @@
import { createMemo } from "solid-js";
import * as React from "react";
import {
ensureMarkdownStyles,
@ -13,7 +13,7 @@ export type TextProps = {
export default function Text(props: TextProps) {
ensureMarkdownStyles();
const blocks = createMemo(() => parseMarkdownBlocks(props.value));
const blocks = React.useMemo(() => parseMarkdownBlocks(props.value), [props.value]);
return <div class="tm-md-root">{renderBlocks(blocks())}</div>;
return <div className="tm-md-root">{renderBlocks(blocks)}</div>;
}

View file

@ -3,8 +3,7 @@
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true

View file

@ -15,6 +15,7 @@
"dependencies": {
"@tensamin/shared": "workspace:*",
"@tensamin/ui": "workspace:*",
"solid-js": "^1.9.11"
"react": "^19.2.0",
"react-dom": "^19.2.0"
}
}

View file

@ -1,17 +1,10 @@
import {
createContext,
createSignal,
Show,
useContext,
type ParentProps,
} from "solid-js";
import * as React from "react";
import {
type Storage as StorageSchema,
storageDefaults as defaults,
} from "@tensamin/shared/data";
import { getEntry, setEntry, deleteEntry } from "./indexed-db";
import ErrorScreen from "@tensamin/ui/screens/error";
import { createStore } from "solid-js/store";
import { log } from "@tensamin/shared/log";
interface StorageContextValue {
@ -23,20 +16,27 @@ interface StorageContextValue {
clear: () => Promise<void>;
}
const StorageContext = createContext<StorageContextValue>();
const StorageContext = React.createContext<StorageContextValue | undefined>(
undefined,
);
const isIndexedDBSupported = typeof indexedDB !== "undefined";
export default function StorageProvider(props: ParentProps) {
const [storage, setStorage] = createStore<StorageSchema>(defaults);
export default function StorageProvider(props: { children: React.ReactNode }) {
const [storage, setStorage] = React.useState<StorageSchema>(defaults);
const storageRef = React.useRef(storage);
const [error, setError] = createSignal<string>("");
const [errorDescription, setErrorDescription] = createSignal<string>("");
const [error, setError] = React.useState("");
const [errorDescription, setErrorDescription] = React.useState("");
React.useEffect(() => {
storageRef.current = storage;
}, [storage]);
const loadIO = React.useCallback(
async <K extends keyof StorageSchema>(key: K): Promise<StorageSchema[K]> => {
let stored: StorageSchema[K] | undefined;
async function loadIO<K extends keyof StorageSchema>(
key: K,
): Promise<StorageSchema[K]> {
let stored;
try {
stored = await getEntry(key);
} catch (err) {
@ -46,84 +46,89 @@ export default function StorageProvider(props: ParentProps) {
);
log(0, "Storage", "red", err);
}
if (stored !== undefined) {
setStorage(key, stored);
setStorage((prev) => ({ ...prev, [key]: stored }));
return stored;
}
return defaults[key];
}
async function saveIO<K extends keyof StorageSchema>(
return defaults[key];
},
[],
);
const saveIO = React.useCallback(
async <K extends keyof StorageSchema>(
key: K,
value: StorageSchema[K],
): Promise<void> {
): Promise<void> => {
if (JSON.stringify(value) === JSON.stringify(defaults[key])) {
await deleteEntry(key);
setStorage(key, defaults[key]);
setStorage((prev) => ({ ...prev, [key]: defaults[key] }));
} else {
await setEntry(key, value);
setStorage(key, value);
}
setStorage((prev) => ({ ...prev, [key]: value }));
}
},
[],
);
const value = React.useMemo<StorageContextValue>(
() => ({
async load<K extends keyof StorageSchema>(key: K): Promise<StorageSchema[K]> {
const current = storageRef.current[key];
const value: StorageContextValue = {
async load<K extends keyof StorageSchema>(
key: K,
): Promise<StorageSchema[K]> {
if (
storage[key] === undefined ||
JSON.stringify(storage[key]) === JSON.stringify(defaults[key])
current === undefined ||
JSON.stringify(current) === JSON.stringify(defaults[key])
) {
const value = await loadIO(key);
setStorage(key, value);
const loadedValue = await loadIO(key);
setStorage((prev) => ({ ...prev, [key]: loadedValue }));
return loadedValue;
}
return storage[key];
return current;
},
async save<K extends keyof StorageSchema>(
key: K,
value: StorageSchema[K],
nextValue: StorageSchema[K],
): Promise<void> {
await saveIO(key, value);
setStorage(key, value);
await saveIO(key, nextValue);
},
async clear() {
const keys = Object.keys(defaults) as (keyof StorageSchema)[];
await Promise.all(keys.map((key) => deleteEntry(key)));
setStorage(defaults);
},
};
}),
[loadIO, saveIO],
);
React.useEffect(() => {
// @ts-expect-error development utility
window.save = value.save;
}, [value]);
if (error !== "" && errorDescription !== "") {
return <ErrorScreen error={error} description={errorDescription} />;
}
if (!isIndexedDBSupported) {
return (
<Show
when={error() !== "" && errorDescription() !== ""}
fallback={
<Show
when={isIndexedDBSupported}
fallback={
<ErrorScreen
error="Unsupported Browser"
description="Your browser does not support IndexedDB, which is required for this application to function."
/>
}
>
<StorageContext.Provider value={value}>
{props.children}
</StorageContext.Provider>
</Show>
}
>
<ErrorScreen error={error()} description={errorDescription()} />
</Show>
);
}
return <StorageContext.Provider value={value}>{props.children}</StorageContext.Provider>;
}
export function useStorage(): StorageContextValue {
const context = useContext(StorageContext);
const context = React.useContext(StorageContext);
if (!context) {
throw new Error("useStorage must be used within a StorageProvider");
}

View file

@ -3,8 +3,7 @@
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true

View file

@ -15,12 +15,13 @@
"build": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"@solidjs/router": "^0.15.4",
"@tanstack/react-router": "^1.0.0",
"@tensamin/core-crypto": "workspace:*",
"@tensamin/core-storage": "workspace:*",
"@tensamin/shared": "workspace:*",
"@tensamin/ui": "workspace:*",
"solid-js": "^1.9.10",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"zod": "^4.3.6"
},
"devDependencies": {

View file

@ -1,15 +1,5 @@
import * as React from "react";
import { log } from "@tensamin/shared/log";
import {
createContext,
createEffect,
createSignal,
onCleanup,
Show,
untrack,
useContext,
type ParentProps,
} from "solid-js";
import { createTransportClient, READY_STATE, type BoundSendFn } from "./core";
import {
PING_INTERVAL,
@ -21,17 +11,8 @@ import {
socket as schemas,
type Socket as Schemas,
} from "@tensamin/shared/data";
import { useStorage } from "@tensamin/core-storage/context";
import { useCrypto } from "@tensamin/core-crypto/context";
import Loading from "@tensamin/ui/screens/loading";
import ErrorScreen from "@tensamin/ui/screens/error";
import { useNavigate } from "@solidjs/router";
type OmikronData = {
id: number;
public_key: string;
ip_address: string;
};
type ContextType = {
send: BoundSendFn<Schemas>;
@ -40,228 +21,30 @@ type ContextType = {
iotaPing: () => number;
};
const socketContext = createContext<ContextType>();
const socketContext = React.createContext<ContextType | undefined>(undefined);
export default function Provider(props: ParentProps) {
const [omikron, setOmikron] = createSignal<OmikronData | null>(null);
const [readyState, setReadyState] = createSignal<number>(READY_STATE.CLOSED);
const [identified, setIdentified] = createSignal<boolean>(false);
export default function Provider(props: { children: React.ReactNode }) {
const [readyState, setReadyState] = React.useState<number>(READY_STATE.CLOSED);
const [connected, setConnected] = React.useState<boolean>(false);
const [ownPing, setOwnPing] = createSignal<number>(0);
const [iotaPing, setIotaPing] = createSignal<number>(0);
const [ownPing, setOwnPing] = React.useState<number>(0);
const [iotaPing, setIotaPing] = React.useState<number>(0);
const [error, setError] = createSignal<string>("");
const [errorDescription, setErrorDescription] = createSignal<string>("");
const [error, setError] = React.useState("");
const [errorDescription, setErrorDescription] = React.useState("");
const { load } = useStorage();
const { get_shared_secret, decrypt } = useCrypto();
const clientRef = React.useRef<ReturnType<
typeof createTransportClient<Schemas>
> | null>(null);
const navigate = useNavigate();
let client: ReturnType<typeof createTransportClient<Schemas>> | null = null;
// Load Omikron
createEffect(() => {
if (omikron()) return;
const controller = new AbortController();
(async () => {
try {
const userId = await load("user_id");
// Redirect to login
if (userId === 0) {
navigate("/login");
return;
}
const res = await fetch(
"https://omega.tensamin.net/api/get/omikron/" + String(userId),
{ signal: controller.signal },
);
const data = await res.json();
setOmikron(data);
} catch (e) {
if (controller.signal.aborted) return;
setError("Failed to load Omikron data");
setErrorDescription(
"An error occurred while fetching the Omikron server data. Please try again later.",
);
log(0, "Socket", "red", "Failed to fetch Omikron data", e);
}
})();
onCleanup(() => controller.abort());
});
createEffect(() => {
if (identified()) {
const interval = setInterval(async () => {
try {
const originalNow = Date.now();
const data = await send("ping", {
last_ping: originalNow,
});
const travelTime = Date.now() - originalNow;
setOwnPing(travelTime);
setIotaPing(data.data.ping_iota);
} catch (error) {
log(1, "Socket", "yellow", "Ping failed", error);
}
}, PING_INTERVAL);
onCleanup(() => clearInterval(interval));
}
});
// Create connection
createEffect(() => {
if (!omikron()) return;
let attempts = 0;
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
let disposed = false;
const transportClient = createTransportClient(schemas, {
url: TRANSPORT_URL,
onReadyStateChange: setReadyState,
onClose: ({ error: closeError, intentional }) => {
setIdentified(false);
if (disposed || intentional) {
return;
}
log(0, "Socket", "red", "Disconnected", closeError);
if (attempts < RETRY_COUNT) {
attempts += 1;
reconnectTimer = setTimeout(() => {
void connect();
}, RETRY_INTERVAL);
return;
}
setError("Connection Failed");
setErrorDescription(
"Unable to connect to the server after multiple attempts. Please check your internet connection or try again later.",
);
log(0, "Socket", "red", "Reconnection attempts exhausted", closeError);
},
});
client = transportClient;
async function identify(activeClient: typeof transportClient) {
const userId = await load("user_id");
const privateKey = await load("private_key");
const currentOmikron = untrack(() => omikron());
activeClient
.send("identification", { user_id: userId })
.then(async (data) => {
const ownUserData = await activeClient.send("get_user_data", {
user_id: userId,
});
if (!currentOmikron?.public_key) {
setError("Omikron data missing");
setErrorDescription(
"Omikron server data is missing. Please try again later.",
);
log(0, "Socket", "red", "Omikron public key missing");
return;
}
try {
const sharedSecret = await get_shared_secret(
privateKey,
ownUserData.data.public_key,
currentOmikron.public_key,
);
const solvedChallenge = await decrypt(
sharedSecret,
data.data.challenge,
);
activeClient
.send("challenge_response", {
challenge: btoa(solvedChallenge),
})
.then(() => {
if (client !== activeClient || disposed) {
return;
}
log(1, "Socket", "green", "Identification successful");
setIdentified(true);
setError("");
setErrorDescription("");
})
.catch((error) => {
log(0, "Socket", "red", "Challenge failed", error);
setError("Challenge Failed");
setErrorDescription(
"Failed to respond to the server's challenge. Please try again later.",
);
});
} catch (err) {
setError("Validation Failed");
setErrorDescription(
"Failed to validate the server's identity. Please try again later.",
);
log(0, "Socket", "red", "Server identity validation failed", err);
}
})
.catch((e) => {
log(0, "Socket", "red", "Identification failed", e);
setError("Identification Failed");
setErrorDescription(
"Failed to identify with the server. Please try again later.",
);
});
}
async function connect() {
if (disposed) return;
try {
await transportClient.connect(TRANSPORT_URL);
attempts = 0;
await identify(transportClient);
} catch (error) {
if (!disposed) {
log(0, "Socket", "red", "Connection attempt failed", error);
}
}
}
void connect();
onCleanup(() => {
disposed = true;
if (reconnectTimer) clearTimeout(reconnectTimer);
if (client === transportClient) {
client = null;
}
void transportClient.close("context-dispose");
setReadyState(READY_STATE.CLOSED);
setIdentified(false);
});
});
// Create Send Function
const send: BoundSendFn<Schemas> = ((
const send = React.useCallback<BoundSendFn<Schemas>>(
((
type: string,
data?: Record<string, unknown>,
options?: { id?: number; noResponse?: boolean },
) => {
const client = clientRef.current;
if (!client) {
return Promise.reject(new Error("Socket is not connected"));
}
@ -272,41 +55,183 @@ export default function Provider(props: ParentProps) {
noResponse: true,
});
}
return client.send(type as keyof Schemas & string, data as never, {
...options,
noResponse: false,
});
}) as BoundSendFn<Schemas>;
}) as BoundSendFn<Schemas>,
[],
);
const progress = () => {
if (!omikron()) return 40;
if (readyState() !== READY_STATE.OPEN) return 70;
if (!identified()) return 90;
return 100;
React.useEffect(() => {
if (!connected) {
return;
}
const interval = setInterval(async () => {
try {
const originalNow = Date.now();
const data = await send("ping", {
last_ping: originalNow,
});
const travelTime = Date.now() - originalNow;
setOwnPing(travelTime);
const remotePing = data.data.ping_iota;
if (typeof remotePing === "number") {
setIotaPing(remotePing);
}
} catch (intervalError) {
log(1, "Socket", "yellow", "Ping failed", intervalError);
}
}, PING_INTERVAL);
return () => {
clearInterval(interval);
};
}, [connected, send]);
React.useEffect(() => {
let attempts = 0;
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
let reconnectScheduled = false;
let disposed = false;
const clearReconnectTimer = () => {
if (!reconnectTimer) {
return;
}
clearTimeout(reconnectTimer);
reconnectTimer = null;
reconnectScheduled = false;
};
return (
<Show
when={error() === "" && errorDescription() === ""}
fallback={
<ErrorScreen error={error()} description={errorDescription()} />
const scheduleReconnect = (reason?: unknown) => {
if (disposed || reconnectScheduled) {
return;
}
>
<Show
when={omikron() && identified()}
fallback={<Loading progress={progress()} />}
>
<socketContext.Provider value={{ send, readyState, ownPing, iotaPing }}>
if (attempts >= RETRY_COUNT) {
setError("Connection Failed");
setErrorDescription(
"Unable to connect to the server after multiple attempts. Please check your internet connection or try again later.",
);
log(0, "Socket", "red", "Reconnection attempts exhausted", reason);
return;
}
attempts += 1;
reconnectScheduled = true;
reconnectTimer = setTimeout(() => {
reconnectScheduled = false;
reconnectTimer = null;
void connect();
}, RETRY_INTERVAL);
};
const transportClient = createTransportClient(schemas, {
url: TRANSPORT_URL,
onReadyStateChange: (state) => {
setReadyState(state);
if (state === READY_STATE.OPEN) {
attempts = 0;
clearReconnectTimer();
setConnected(true);
setError("");
setErrorDescription("");
log(1, "Socket", "green", "Connected");
return;
}
setConnected(false);
},
onClose: ({ error: closeError, intentional }) => {
setConnected(false);
if (disposed || intentional) {
return;
}
log(0, "Socket", "red", "Disconnected", closeError);
scheduleReconnect(closeError);
},
});
clientRef.current = transportClient;
async function connect() {
if (disposed) {
return;
}
try {
await transportClient.connect(TRANSPORT_URL);
} catch (connectError) {
if (disposed) {
return;
}
log(0, "Socket", "red", "Connection attempt failed", connectError);
scheduleReconnect(connectError);
}
}
void connect();
return () => {
disposed = true;
clearReconnectTimer();
if (clientRef.current === transportClient) {
clientRef.current = null;
}
void transportClient.close("context-dispose");
setReadyState(READY_STATE.CLOSED);
setConnected(false);
};
}, []);
const progress = React.useMemo(() => {
if (readyState === READY_STATE.CONNECTING) return 70;
if (!connected) return 90;
return 100;
}, [connected, readyState]);
const contextValue = React.useMemo<ContextType>(
() => ({
send,
readyState: () => readyState,
ownPing: () => ownPing,
iotaPing: () => iotaPing,
}),
[iotaPing, ownPing, readyState, send],
);
if (error !== "" && errorDescription !== "") {
return <ErrorScreen error={error} description={errorDescription} />;
}
if (!connected) {
return <Loading progress={progress} />;
}
return (
<socketContext.Provider value={contextValue}>
{props.children}
</socketContext.Provider>
</Show>
</Show>
);
}
export function useSocket(): ContextType {
const context = useContext(socketContext);
if (!context)
const context = React.useContext(socketContext);
if (!context) {
throw new Error("useSocket must be used within a SocketProvider");
}
return context;
}

View file

@ -15,6 +15,14 @@ const APPLICATION_CLOSE_CODE = 0;
const APPLICATION_CLOSE_REASON = "epsilon-close";
const MAX_REQUEST_ID = 0xffff_fffe;
const DATA_VALUE_KIND_BOOL_TRUE = 0x01;
const DATA_VALUE_KIND_BOOL_FALSE = 0x02;
const DATA_VALUE_KIND_NUMBER = 0x03;
const DATA_VALUE_KIND_STRING = 0x04;
const DATA_VALUE_KIND_ARRAY = 0x05;
const DATA_VALUE_KIND_CONTAINER = 0x06;
const DATA_VALUE_KIND_NULL = 0x07;
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
@ -966,34 +974,40 @@ function encodeCommunicationMessage(
message: TypedMessage<Record<string, unknown>>,
) {
const typeIndex = parseCommunicationType(message.type);
const dataBuffer = encodeValueForKind("container", message.data, "payload");
const hasId = message.id !== 0;
const totalLength = 2 + (hasId ? 4 : 0) + 4 + dataBuffer.byteLength;
const buffer = new Uint8Array(totalLength);
const dataBuffer = encodeContainerPayload(message.data, "payload");
const payloadLength = 2 + (hasId ? 4 : 0) + dataBuffer.byteLength;
const buffer = new Uint8Array(4 + payloadLength);
buffer[0] = typeIndex;
buffer[1] = hasId ? 0b0010_0000 : 0;
writeU32(buffer, 0, payloadLength);
buffer[4] = typeIndex;
buffer[5] = hasId ? 0b0000_0100 : 0;
let offset = 2;
let offset = 6;
if (hasId) {
writeU32(buffer, offset, message.id);
offset += 4;
}
writeU32(buffer, offset, dataBuffer.byteLength);
offset += 4;
buffer.set(dataBuffer, offset);
return buffer;
}
function decodeCommunicationMessage(payload: Uint8Array): TypedMessage {
const reader = new ByteReader(payload);
function decodeCommunicationMessage(frame: Uint8Array): TypedMessage {
const reader = new ByteReader(frame);
const payloadLength = reader.readU32();
if (payloadLength !== frame.byteLength - 4) {
throw new Error(
`Communication payload length mismatch: expected ${payloadLength}, received ${frame.byteLength - 4}`,
);
}
const typeIndex = reader.readU8();
const flags = reader.readU8();
const hasSender = (flags & 0b1000_0000) !== 0;
const hasReceiver = (flags & 0b0100_0000) !== 0;
const hasId = (flags & 0b0010_0000) !== 0;
const hasSender = (flags & 0b0000_0001) !== 0;
const hasReceiver = (flags & 0b0000_0010) !== 0;
const hasId = (flags & 0b0000_0100) !== 0;
const id = hasId ? reader.readU32() : 0;
if (hasSender) {
@ -1003,11 +1017,18 @@ function decodeCommunicationMessage(payload: Uint8Array): TypedMessage {
reader.readU48();
}
const dataLength = reader.readU32();
const dataBytes = reader.readBytes(dataLength);
const decodedData = decodeValue(new ByteReader(dataBytes));
if (!isPlainObject(decodedData)) {
throw new Error("Protocol payload container was not decoded as an object");
const consumedHeaderBytes =
2 + (hasId ? 4 : 0) + (hasSender ? 6 : 0) + (hasReceiver ? 6 : 0);
if (consumedHeaderBytes > payloadLength) {
throw new Error("Communication header exceeds payload length");
}
const dataLength = payloadLength - consumedHeaderBytes;
const dataReader = new ByteReader(reader.readBytes(dataLength));
const decodedData = decodeContainerPayload(dataReader);
if (!dataReader.isAtEnd()) {
throw new Error("Trailing bytes found after communication data payload");
}
if (!reader.isAtEnd()) {
@ -1051,13 +1072,21 @@ function getExpectedKind(type: string) {
return kind;
}
function encodeValueForKind(
type EncodedDataValue = {
kind: number;
payload: Uint8Array;
};
function encodeDataValueForKind(
kind: DataKind,
value: unknown,
path: string,
): Uint8Array {
): EncodedDataValue {
if (typeof kind === "object") {
return encodeArrayValue(kind.array, value, path);
return {
kind: DATA_VALUE_KIND_ARRAY,
payload: encodeArrayPayload(kind.array, value, path),
};
}
switch (kind) {
@ -1066,35 +1095,50 @@ function encodeValueForKind(
throw new Error(`Expected boolean at "${path}"`);
}
return Uint8Array.of(0x03, value ? 1 : 0);
return {
kind: value ? DATA_VALUE_KIND_BOOL_TRUE : DATA_VALUE_KIND_BOOL_FALSE,
payload: new Uint8Array(0),
};
case "number":
return encodeNumberValue(value, path);
return {
kind: DATA_VALUE_KIND_NUMBER,
payload: encodeNumberPayload(value, path),
};
case "string":
if (typeof value !== "string") {
throw new Error(`Expected string at "${path}"`);
}
return encodeStringValue(value);
return {
kind: DATA_VALUE_KIND_STRING,
payload: textEncoder.encode(value),
};
case "container":
if (!isPlainObject(value)) {
throw new Error(`Expected object at "${path}"`);
}
return encodeContainerValue(value, path);
return {
kind: DATA_VALUE_KIND_CONTAINER,
payload: encodeContainerPayload(value, path),
};
case "null":
if (value !== null && value !== undefined) {
throw new Error(`Expected null at "${path}"`);
}
return Uint8Array.of(0x06);
return {
kind: DATA_VALUE_KIND_NULL,
payload: new Uint8Array(0),
};
}
}
function encodeNumberValue(value: unknown, path: string) {
function encodeNumberPayload(value: unknown, path: string) {
if (
typeof value !== "number" ||
!Number.isFinite(value) ||
@ -1103,22 +1147,12 @@ function encodeNumberValue(value: unknown, path: string) {
throw new Error(`Expected safe integer at "${path}"`);
}
const buffer = new Uint8Array(9);
buffer[0] = 0x01;
writeI64(buffer, 1, value);
const buffer = new Uint8Array(8);
writeI64(buffer, 0, value);
return buffer;
}
function encodeStringValue(value: string) {
const bytes = textEncoder.encode(value);
const buffer = new Uint8Array(5 + bytes.byteLength);
buffer[0] = 0x02;
writeU32(buffer, 1, bytes.byteLength);
buffer.set(bytes, 5);
return buffer;
}
function encodeArrayValue(
function encodeArrayPayload(
innerKind: PrimitiveDataKind | "container" | "null",
value: unknown,
path: string,
@ -1132,28 +1166,44 @@ function encodeArrayValue(
}
const encodedItems = value.map((entry, index) =>
encodeValueForKind(innerKind, entry, `${path}[${index}]`),
encodeDataValueForKind(innerKind, entry, `${path}[${index}]`),
);
const byteLength = encodedItems.reduce(
(sum, item) => sum + item.byteLength,
0,
);
const buffer = new Uint8Array(4 + byteLength);
buffer[0] = 0x04;
buffer[1] = value.length === 0 ? 0x00 : kindToMarker(innerKind);
writeU16(buffer, 2, value.length);
let totalLength = 2;
for (const encodedItem of encodedItems) {
totalLength += 1;
let offset = 4;
if (!isBoolKindMarker(encodedItem.kind)) {
if (encodedItem.payload.byteLength > 0xffff) {
throw new Error(`Array item at "${path}" is too large for protocol encoding`);
}
totalLength += 2 + encodedItem.payload.byteLength;
}
}
const buffer = new Uint8Array(totalLength);
writeU16(buffer, 0, value.length);
let offset = 2;
for (const item of encodedItems) {
buffer.set(item, offset);
offset += item.byteLength;
buffer[offset] = item.kind;
offset += 1;
if (isBoolKindMarker(item.kind)) {
continue;
}
writeU16(buffer, offset, item.payload.byteLength);
offset += 2;
buffer.set(item.payload, offset);
offset += item.payload.byteLength;
}
return buffer;
}
function encodeContainerValue(value: Record<string, unknown>, path: string) {
function encodeContainerPayload(value: Record<string, unknown>, path: string) {
const normalizedEntries = new Map<
string,
{ index: number; value: unknown }
@ -1171,82 +1221,103 @@ function encodeContainerValue(value: Record<string, unknown>, path: string) {
([, left], [, right]) => left.index - right.index,
);
const encodedEntries: Uint8Array[] = [];
let totalLength = 3;
if (entries.length > 0xffff) {
throw new Error(`Container at "${path}" has too many entries for protocol encoding`);
}
const encodedEntries: Array<{ keyIndex: number; value: EncodedDataValue }> = [];
let totalLength = 2;
for (const [name, entry] of entries) {
const expectedKind = getExpectedKind(name);
const pathForEntry = `${path}.${name}`;
if (expectedKind === "bool") {
if (typeof entry.value !== "boolean") {
throw new Error(`Expected boolean at "${pathForEntry}"`);
}
const encoded = Uint8Array.of(entry.index, entry.value ? 1 : 0);
encodedEntries.push(encoded);
totalLength += encoded.byteLength;
continue;
}
const encodedValue = encodeValueForKind(
const encodedValue = encodeDataValueForKind(
expectedKind,
entry.value,
pathForEntry,
);
const body = encodedValue.subarray(1);
const encoded = new Uint8Array(5 + body.byteLength);
encoded[0] = entry.index;
writeU32(encoded, 1, body.byteLength);
encoded.set(body, 5);
encodedEntries.push(encoded);
totalLength += encoded.byteLength;
if (isBoolKindMarker(encodedValue.kind)) {
totalLength += 2;
} else {
if (encodedValue.payload.byteLength > 0xffff) {
throw new Error(`Container entry "${pathForEntry}" is too large for protocol encoding`);
}
totalLength += 4 + encodedValue.payload.byteLength;
}
encodedEntries.push({
keyIndex: entry.index,
value: encodedValue,
});
}
const buffer = new Uint8Array(totalLength);
buffer[0] = 0x05;
writeU16(buffer, 1, entries.length);
writeU16(buffer, 0, entries.length);
let offset = 3;
for (const encoded of encodedEntries) {
buffer.set(encoded, offset);
offset += encoded.byteLength;
let offset = 2;
for (const entry of encodedEntries) {
buffer[offset] = entry.value.kind;
offset += 1;
if (isBoolKindMarker(entry.value.kind)) {
buffer[offset] = entry.keyIndex;
offset += 1;
continue;
}
writeU16(buffer, offset, entry.value.payload.byteLength);
offset += 2;
buffer[offset] = entry.keyIndex;
offset += 1;
buffer.set(entry.value.payload, offset);
offset += entry.value.payload.byteLength;
}
return buffer;
}
function decodeValue(reader: ByteReader): unknown {
const marker = reader.readU8();
function decodeValuePayload(marker: number, payload: Uint8Array): unknown {
const reader = new ByteReader(payload);
switch (marker) {
case 0x01:
case DATA_VALUE_KIND_BOOL_TRUE:
if (!reader.isAtEnd()) {
throw new Error("Unexpected payload for boolean true value");
}
return true;
case DATA_VALUE_KIND_BOOL_FALSE:
if (!reader.isAtEnd()) {
throw new Error("Unexpected payload for boolean false value");
}
return false;
case DATA_VALUE_KIND_NUMBER:
if (payload.byteLength !== 8) {
throw new Error(`Invalid number payload length ${payload.byteLength}`);
}
return reader.readI64();
case 0x02: {
const length = reader.readU32();
return textDecoder.decode(reader.readBytes(length));
case DATA_VALUE_KIND_STRING:
return textDecoder.decode(payload);
case DATA_VALUE_KIND_ARRAY:
return decodeArrayPayload(reader);
case DATA_VALUE_KIND_CONTAINER:
return decodeContainerPayload(reader);
case DATA_VALUE_KIND_NULL:
if (!reader.isAtEnd()) {
throw new Error("Unexpected payload for null value");
}
case 0x03:
return reader.readU8() !== 0;
case 0x04: {
reader.readU8();
const length = reader.readU16();
const values: unknown[] = [];
for (let index = 0; index < length; index += 1) {
values.push(decodeValue(reader));
}
return values;
}
case 0x05:
return decodeContainer(reader);
case 0x06:
return null;
default:
@ -1254,39 +1325,86 @@ function decodeValue(reader: ByteReader): unknown {
}
}
function decodeContainer(reader: ByteReader) {
const length = reader.readU16();
const value: Record<string, unknown> = {};
function decodeArrayPayload(reader: ByteReader) {
const itemCount = reader.readU16();
const values: unknown[] = [];
for (let index = 0; index < length; index += 1) {
const keyIndex = reader.readU8();
const key = DATA_TYPES[keyIndex];
if (!key) {
throw new Error(`Unknown data type index ${keyIndex}`);
}
for (let index = 0; index < itemCount; index += 1) {
const marker = reader.readU8();
const expectedKind = getExpectedKind(key);
if (expectedKind === "bool") {
value[key] = normalizeIncomingValue(key, reader.readU8() !== 0);
if (isBoolKindMarker(marker)) {
values.push(marker === DATA_VALUE_KIND_BOOL_TRUE);
continue;
}
const encodedLength = reader.readU32();
const encodedValue = reader.readBytes(encodedLength);
const fullValue = new Uint8Array(1 + encodedValue.byteLength);
fullValue[0] = kindToMarker(expectedKind);
fullValue.set(encodedValue, 1);
const payloadLength = reader.readU16();
const payload = reader.readBytes(payloadLength);
values.push(decodeValuePayload(marker, payload));
}
value[key] = normalizeIncomingValue(
key,
decodeValue(new ByteReader(fullValue)),
return values;
}
function decodeContainerPayload(reader: ByteReader) {
const entryCount = reader.readU16();
const value: Record<string, unknown> = {};
for (let index = 0; index < entryCount; index += 1) {
const marker = reader.readU8();
const payloadLength = isBoolKindMarker(marker) ? 0 : reader.readU16();
const keyIndex = reader.readU8();
const key = getDataTypeNameByIndex(keyIndex);
const payload = isBoolKindMarker(marker)
? new Uint8Array(0)
: reader.readBytes(payloadLength);
const expectedKind = getExpectedKind(key);
if (!isMarkerCompatibleWithKind(marker, expectedKind)) {
throw new Error(
`Unexpected marker 0x${marker.toString(16)} for data type "${key}"`,
);
}
value[key] = normalizeIncomingValue(key, decodeValuePayload(marker, payload));
}
return value;
}
function getDataTypeNameByIndex(index: number) {
const key = DATA_TYPES[index];
if (!key) {
throw new Error(`Unknown data type index ${index}`);
}
return key;
}
function isBoolKindMarker(marker: number) {
return (
marker === DATA_VALUE_KIND_BOOL_TRUE || marker === DATA_VALUE_KIND_BOOL_FALSE
);
}
function isMarkerCompatibleWithKind(marker: number, kind: DataKind) {
if (typeof kind === "object") {
return marker === DATA_VALUE_KIND_ARRAY;
}
switch (kind) {
case "bool":
return isBoolKindMarker(marker);
case "number":
return marker === DATA_VALUE_KIND_NUMBER;
case "string":
return marker === DATA_VALUE_KIND_STRING;
case "container":
return marker === DATA_VALUE_KIND_CONTAINER;
case "null":
return marker === DATA_VALUE_KIND_NULL;
}
}
function normalizeOutgoingValue(type: string, value: unknown) {
if (SCALAR_NUMBER_ARRAY_DATA_TYPES.has(type) && typeof value === "number") {
return [value];
@ -1308,25 +1426,6 @@ function normalizeIncomingValue(type: string, value: unknown) {
return value;
}
function kindToMarker(kind: DataKind) {
if (typeof kind === "object") {
return 0x04;
}
switch (kind) {
case "number":
return 0x01;
case "string":
return 0x02;
case "bool":
return 0x03;
case "container":
return 0x05;
case "null":
return 0x06;
}
}
function writeU16(buffer: Uint8Array, offset: number, value: number) {
new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).setUint16(
offset,

View file

@ -3,8 +3,7 @@
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true

View file

@ -16,7 +16,8 @@
"dependencies": {
"@tensamin/ttp": "workspace:*",
"@tensamin/shared": "workspace:*",
"solid-js": "^1.9.11",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"zod": "^4.3.6"
}
}

View file

@ -1,5 +1,4 @@
import { createContext, useContext, type ParentProps } from "solid-js";
import { createStore } from "solid-js/store";
import * as React from "react";
import { useSocket } from "@tensamin/ttp/context";
import { socket as schemas } from "@tensamin/shared/data";
@ -12,15 +11,15 @@ interface contextValue {
get(userId: number): Promise<User>;
}
const UserContext = createContext<contextValue>();
const UserContext = React.createContext<contextValue | undefined>(undefined);
export default function UserProvider(props: ParentProps) {
const [storage, setStorage] = createStore<Record<number, User>>({});
export default function UserProvider(props: { children: React.ReactNode }) {
const storageRef = React.useRef<Record<number, User>>({});
const { send } = useSocket();
async function get(userId: number): Promise<User> {
if (storage[userId] === undefined) {
if (storageRef.current[userId] === undefined) {
try {
const userData = await send("get_user_data", { user_id: userId });
@ -30,12 +29,12 @@ export default function UserProvider(props: ParentProps) {
: undefined;
// Temp end
setStorage(userId, userData.data);
storageRef.current[userId] = userData.data;
} catch {
setStorage(userId, failedUser);
storageRef.current[userId] = failedUser;
}
}
return storage[userId];
return storageRef.current[userId];
}
return (
@ -46,7 +45,7 @@ export default function UserProvider(props: ParentProps) {
}
export function useUser(): contextValue {
const context = useContext(UserContext);
const context = React.useContext(UserContext);
if (!context) {
throw new Error("useUser must be used within a UserProvider");
}

View file

@ -1,16 +1,25 @@
import { createEffect, createSignal, type JSX } from "solid-js";
import * as React from "react";
import { useUser, type User } from "./context";
export default function Wrapper(props: {
userId: number;
component: (user: User) => JSX.Element;
component: (user: User) => React.ReactNode;
}) {
const { get } = useUser();
const [user, setUser] = createSignal<User | null>(null);
const [user, setUser] = React.useState<User | null>(null);
createEffect(() => {
get(props.userId).then(setUser);
React.useEffect(() => {
let active = true;
get(props.userId).then((value) => {
if (active) {
setUser(value);
}
});
return <>{user() ? props.component(user() as User) : null}</>;
return () => {
active = false;
};
}, [get, props.userId]);
return <>{user ? props.component(user) : null}</>;
}

View file

@ -3,8 +3,7 @@
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true

View file

@ -13,10 +13,8 @@
},
"dependencies": {
"@tensamin/ttp": "git+https://github.com/Tensamin/TTP.git",
"@solidjs/router": "^0.15.4",
"lucide-solid": "^0.564.0",
"solid-js": "^1.9.11",
"solid-sonner": "^0.2.8",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"zod": "^4.3.6"
}
}

View file

@ -1,9 +1,8 @@
import { createContext, useContext } from "solid-js";
import type { ParentProps } from "solid-js";
import * as React from "react";
export const context = createContext<contextType>();
export const context = React.createContext<contextType | undefined>(undefined);
export default function Provider(props: ParentProps) {
export default function Provider(props: { children: React.ReactNode }) {
// live_messages
return (
@ -18,7 +17,7 @@ type contextType = {
};
export function useNotifications(): contextType {
const ctx = useContext(context);
const ctx = React.useContext(context);
if (!ctx) {
throw new Error("useNotifications must be used within a ChatProvider");
}

View file

@ -3,8 +3,7 @@
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true

View file

@ -15,9 +15,10 @@
"build": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"lucide-solid": "^0.564.0",
"solid-js": "^1.9.11",
"solid-sonner": "^0.2.8",
"lucide-react": "^0.564.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"sonner": "^1.0.0",
"zod": "^4.3.6"
}
}

View file

@ -1,5 +1,5 @@
import { toast as sonnerToast } from "solid-sonner";
import { Ban, Check, Info, TriangleAlert } from "lucide-solid";
import { toast as sonnerToast } from "sonner";
import { Ban, Check, Info, TriangleAlert } from "lucide-react";
export function log(
logLevel: number,

View file

@ -3,8 +3,7 @@
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true

View file

@ -14,19 +14,13 @@
"build": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"@ark-ui/solid": "^5.34.1",
"@corvu/drawer": "^0.2.4",
"@corvu/otp-field": "^0.1.4",
"@corvu/resizable": "^0.2.5",
"@kobalte/core": "^0.13.11",
"@tanstack/solid-virtual": "^3.13.21",
"@radix-ui/react-context-menu": "^2.2.16",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk-solid": "^1.1.2",
"embla-carousel-solid": "^8.6.0",
"lucide-solid": "^0.564.0",
"solid-js": "^1.9.11",
"solid-sonner": "^0.2.8",
"lucide-react": "^0.564.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"sonner": "^1.0.0",
"tailwind-merge": "^3.5.0"
}
}

View file

@ -1,11 +1,11 @@
import { ExternalLink } from "lucide-solid";
import { ExternalLink } from "lucide-react";
export default function Link(props: { link: string; label: string }) {
return (
<a
target="_blank"
href={props.link}
class="flex gap-1.5 items-center justify-center text-primary border-b hover:border-primary border-transparent"
className="flex gap-1.5 items-center justify-center text-primary border-b hover:border-primary border-transparent"
>
<ExternalLink size={17} /> {props.label}
</a>

View file

@ -2,9 +2,9 @@ import Link from "../link";
export default function Screen(props: { error: string; description: string }) {
return (
<div class="bg-background w-full h-screen flex flex-col justify-center items-center">
<p class="text-3xl font-bold">{props.error}</p>
<p class="pt-4 text-lg w-1/2 text-center text-muted-foreground pb-8">
<div className="bg-background w-full h-screen flex flex-col justify-center items-center">
<p className="text-3xl font-bold">{props.error}</p>
<p className="pt-4 text-lg w-1/2 text-center text-muted-foreground pb-8">
{props.description}
</p>
<Link label="status.tensamin.net" link="https://status.tensamin.net" />

View file

@ -1,42 +1,53 @@
import { createSignal, createEffect, on } from "solid-js";
import * as React from "react";
const DELAY = 250;
export default function Screen(props: { progress: number }) {
const [displayProgress, setDisplayProgress] = createSignal(0);
const [displayProgress, setDisplayProgress] = React.useState(0);
const displayProgressRef = React.useRef(0);
// Animation
createEffect(
on(
() => props.progress,
(target) => {
const start = displayProgress();
React.useEffect(() => {
displayProgressRef.current = displayProgress;
}, [displayProgress]);
React.useEffect(() => {
const target = props.progress;
const start = displayProgressRef.current;
const delta = target - start;
if (delta === 0) return;
if (delta === 0) {
return;
}
const duration = DELAY;
const startTime = performance.now();
let frameId = 0;
function animate(now: number) {
const elapsed = now - startTime;
const t = Math.min(elapsed / duration, 1);
const eased = 1 - Math.pow(1 - t, 3);
setDisplayProgress(start + delta * eased);
if (t < 1) requestAnimationFrame(animate);
if (t < 1) {
frameId = requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
},
),
);
frameId = requestAnimationFrame(animate);
return () => {
cancelAnimationFrame(frameId);
};
}, [props.progress]);
return (
<div class="bg-background w-full h-screen flex flex-col justify-center items-center">
<div class="w-64 h-1.5 bg-secondary rounded-full overflow-hidden relative">
<div className="bg-background w-full h-screen flex flex-col justify-center items-center">
<div className="w-64 h-1.5 bg-secondary rounded-full overflow-hidden relative">
<div
class="h-full bg-primary absolute left-0 top-0"
className="h-full bg-primary absolute left-0 top-0"
style={{
width: `${displayProgress()}%`,
width: `${displayProgress}%`,
}}
/>
</div>

View file

@ -1,97 +0,0 @@
import { cn } from "../libs/cn";
import type {
AccordionContentProps,
AccordionItemProps,
AccordionTriggerProps,
} from "@kobalte/core/accordion";
import { Accordion as AccordionPrimitive } from "@kobalte/core/accordion";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import { type ParentProps, type ValidComponent, splitProps } from "solid-js";
export const Accordion = AccordionPrimitive;
type accordionItemProps<T extends ValidComponent = "div"> =
AccordionItemProps<T> & {
class?: string;
};
export const AccordionItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, accordionItemProps<T>>,
) => {
const [local, rest] = splitProps(props as accordionItemProps, ["class"]);
return (
<AccordionPrimitive.Item class={cn("border-b", local.class)} {...rest} />
);
};
type accordionTriggerProps<T extends ValidComponent = "button"> = ParentProps<
AccordionTriggerProps<T> & {
class?: string;
}
>;
export const AccordionTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, accordionTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as accordionTriggerProps, [
"class",
"children",
]);
return (
<AccordionPrimitive.Header class="flex" as="div">
<AccordionPrimitive.Trigger
class={cn(
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-shadow hover:underline focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring [&[data-expanded]>svg]:rotate-180",
local.class,
)}
{...rest}
>
{local.children}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4 text-muted-foreground transition-transform duration-200"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m6 9l6 6l6-6"
/>
<title>Arrow</title>
</svg>
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
);
};
type accordionContentProps<T extends ValidComponent = "div"> = ParentProps<
AccordionContentProps<T> & {
class?: string;
}
>;
export const AccordionContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, accordionContentProps<T>>,
) => {
const [local, rest] = splitProps(props as accordionContentProps, [
"class",
"children",
]);
return (
<AccordionPrimitive.Content
class={cn(
"animate-accordion-up overflow-hidden text-sm data-[expanded]:animate-accordion-down",
local.class,
)}
{...rest}
>
<div class="pb-4 pt-0">{local.children}</div>
</AccordionPrimitive.Content>
);
};

View file

@ -1,152 +0,0 @@
import { cn } from "../libs/cn";
import type {
AlertDialogCloseButtonProps,
AlertDialogContentProps,
AlertDialogDescriptionProps,
AlertDialogTitleProps,
} from "@kobalte/core/alert-dialog";
import { AlertDialog as AlertDialogPrimitive } from "@kobalte/core/alert-dialog";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ComponentProps, ParentProps, ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
import { buttonVariants } from "./button";
export const AlertDialog = AlertDialogPrimitive;
export const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
type alertDialogContentProps<T extends ValidComponent = "div"> = ParentProps<
AlertDialogContentProps<T> & {
class?: string;
}
>;
export const AlertDialogContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, alertDialogContentProps<T>>,
) => {
const [local, rest] = splitProps(props as alertDialogContentProps, [
"class",
"children",
]);
return (
<AlertDialogPrimitive.Portal>
<AlertDialogPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 bg-background/80 data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0",
)}
/>
<AlertDialogPrimitive.Content
class={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg data-[closed]:duration-200 data-[expanded]:duration-200 data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95 data-[closed]:slide-out-to-left-1/2 data-[closed]:slide-out-to-top-[48%] data-[expanded]:slide-in-from-left-1/2 data-[expanded]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
local.class,
)}
{...rest}
>
{local.children}
</AlertDialogPrimitive.Content>
</AlertDialogPrimitive.Portal>
);
};
export const AlertDialogHeader = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div
class={cn(
"flex flex-col space-y-2 text-center sm:text-left",
local.class,
)}
{...rest}
/>
);
};
export const AlertDialogFooter = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div
class={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
local.class,
)}
{...rest}
/>
);
};
type alertDialogTitleProps<T extends ValidComponent = "h2"> =
AlertDialogTitleProps<T> & {
class?: string;
};
export const AlertDialogTitle = <T extends ValidComponent = "h2">(
props: PolymorphicProps<T, alertDialogTitleProps<T>>,
) => {
const [local, rest] = splitProps(props as alertDialogTitleProps, ["class"]);
return (
<AlertDialogPrimitive.Title
class={cn("text-lg font-semibold", local.class)}
{...rest}
/>
);
};
type alertDialogDescriptionProps<T extends ValidComponent = "p"> =
AlertDialogDescriptionProps<T> & {
class?: string;
};
export const AlertDialogDescription = <T extends ValidComponent = "p">(
props: PolymorphicProps<T, alertDialogDescriptionProps<T>>,
) => {
const [local, rest] = splitProps(props as alertDialogDescriptionProps, [
"class",
]);
return (
<AlertDialogPrimitive.Description
class={cn("text-sm text-muted-foreground", local.class)}
{...rest}
/>
);
};
type alertDialogCloseProps<T extends ValidComponent = "button"> =
AlertDialogCloseButtonProps<T> & {
class?: string;
};
export const AlertDialogClose = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, alertDialogCloseProps<T>>,
) => {
const [local, rest] = splitProps(props as alertDialogCloseProps, ["class"]);
return (
<AlertDialogPrimitive.CloseButton
class={cn(
buttonVariants({
variant: "outline",
}),
"mt-2 md:mt-0",
local.class,
)}
{...rest}
/>
);
};
export const AlertDialogAction = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, alertDialogCloseProps<T>>,
) => {
const [local, rest] = splitProps(props as alertDialogCloseProps, ["class"]);
return (
<AlertDialogPrimitive.CloseButton
class={cn(buttonVariants(), local.class)}
{...rest}
/>
);
};

View file

@ -1,66 +0,0 @@
import { cn } from "../libs/cn";
import type { AlertRootProps } from "@kobalte/core/alert";
import { Alert as AlertPrimitive } from "@kobalte/core/alert";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import type { ComponentProps, ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
export const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm [&:has(svg)]:pl-11 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
},
);
type alertProps<T extends ValidComponent = "div"> = AlertRootProps<T> &
VariantProps<typeof alertVariants> & {
class?: string;
};
export const Alert = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, alertProps<T>>,
) => {
const [local, rest] = splitProps(props as alertProps, ["class", "variant"]);
return (
<AlertPrimitive
class={cn(
alertVariants({
variant: props.variant,
}),
local.class,
)}
{...rest}
/>
);
};
export const AlertTitle = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div
class={cn("font-medium leading-5 tracking-tight", local.class)}
{...rest}
/>
);
};
export const AlertDescription = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div class={cn("text-sm [&_p]:leading-relaxed", local.class)} {...rest} />
);
};

View file

@ -1,10 +1,20 @@
import { Avatar as ArkAvatar } from "@ark-ui/solid/avatar";
import * as React from "react";
export default function Avatar(props: { img?: string; fallback: string }) {
const [imageFailed, setImageFailed] = React.useState(false);
return (
<ArkAvatar.Root class="m-0 bg-card rounded-full border border-input/75 size-9 aspect-square flex items-center justify-center select-none">
<ArkAvatar.Fallback>{props.fallback}</ArkAvatar.Fallback>
<ArkAvatar.Image class="rounded-full" src={props.img} />
</ArkAvatar.Root>
<div className="m-0 bg-card rounded-full border border-input/75 size-9 aspect-square flex items-center justify-center select-none overflow-hidden">
{props.img && !imageFailed ? (
<img
className="rounded-full w-full h-full object-cover"
src={props.img}
alt={props.fallback}
onError={() => setImageFailed(true)}
/>
) : (
<span>{props.fallback}</span>
)}
</div>
);
}

View file

@ -1,42 +0,0 @@
import { cn } from "../libs/cn";
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import { type ComponentProps, splitProps } from "solid-js";
export const badgeVariants = cva(
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-shadow focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
);
export const Badge = (
props: ComponentProps<"div"> & VariantProps<typeof badgeVariants>,
) => {
const [local, rest] = splitProps(props, ["class", "variant"]);
return (
<div
class={cn(
badgeVariants({
variant: local.variant,
}),
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,11 +1,7 @@
import { cn } from "../libs/cn";
import type { ButtonRootProps } from "@kobalte/core/button";
import { Button as ButtonPrimitive } from "@kobalte/core/button";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import type { ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
import * as React from "react";
export const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-[color,background-color,box-shadow] focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
@ -37,30 +33,21 @@ export const buttonVariants = cva(
},
);
type buttonProps<T extends ValidComponent = "button"> = ButtonRootProps<T> &
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
VariantProps<typeof buttonVariants> & {
class?: string;
className?: string;
};
export const Button = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, buttonProps<T>>,
) => {
const [local, rest] = splitProps(props as buttonProps, [
"class",
"variant",
"size",
]);
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, ...props }, ref) => {
return (
<ButtonPrimitive
class={cn(
buttonVariants({
size: local.size,
variant: local.variant,
}),
local.class,
)}
{...rest}
<button
ref={ref}
className={cn(buttonVariants({ variant, size }), className)}
{...props}
/>
);
};
},
);
Button.displayName = "Button";

View file

@ -1,60 +1,35 @@
import { cn } from "../libs/cn";
import type { ComponentProps, ParentComponent } from "solid-js";
import { splitProps } from "solid-js";
import * as React from "react";
export const Card = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
export const Card = ({ className, ...props }: React.ComponentProps<"div">) => (
<div
class={cn(
"rounded-xl border bg-card text-card-foreground shadow",
local.class,
)}
{...rest}
className={cn("rounded-xl border bg-card text-card-foreground shadow", className)}
{...props}
/>
);
};
export const CardHeader = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div class={cn("flex flex-col space-y-1.5 p-6", local.class)} {...rest} />
export const CardHeader = ({ className, ...props }: React.ComponentProps<"div">) => (
<div className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
);
};
export const CardTitle: ParentComponent<ComponentProps<"h1">> = (props) => {
const [local, rest] = splitProps(props, ["class"]);
return (
export const CardTitle = ({ className, ...props }: React.ComponentProps<"h1">) => (
<h1
class={cn("font-semibold leading-none tracking-tight", local.class)}
{...rest}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
);
};
export const CardDescription: ParentComponent<ComponentProps<"h3">> = (
props,
) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<h3 class={cn("text-sm text-muted-foreground", local.class)} {...rest} />
export const CardDescription = ({
className,
...props
}: React.ComponentProps<"h3">) => (
<h3 className={cn("text-sm text-muted-foreground", className)} {...props} />
);
};
export const CardContent = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
export const CardContent = ({ className, ...props }: React.ComponentProps<"div">) => (
<div className={cn("p-6 pt-0", className)} {...props} />
);
return <div class={cn("p-6 pt-0", local.class)} {...rest} />;
};
export const CardFooter = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div class={cn("flex items-center p-6 pt-0", local.class)} {...rest} />
export const CardFooter = ({ className, ...props }: React.ComponentProps<"div">) => (
<div className={cn("flex items-center p-6 pt-0", className)} {...props} />
);
};

View file

@ -1,272 +0,0 @@
import { cn } from "../libs/cn";
import type { CreateEmblaCarouselType } from "embla-carousel-solid";
import createEmblaCarousel from "embla-carousel-solid";
import type {
Accessor,
ComponentProps,
ParentProps,
VoidProps,
} from "solid-js";
import {
createContext,
createEffect,
createMemo,
createSignal,
mergeProps,
onCleanup,
splitProps,
useContext,
} from "solid-js";
import { Button } from "./button";
export type CarouselApi = CreateEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof createEmblaCarousel>;
type CarouselOptions = NonNullable<UseCarouselParameters[0]>;
type CarouselPlugin = NonNullable<UseCarouselParameters[1]>;
type CarouselProps = {
opts?: ReturnType<CarouselOptions>;
plugins?: ReturnType<CarouselPlugin>;
orientation?: "horizontal" | "vertical";
setApi?: (api: CarouselApi) => void;
};
type CarouselContextProps = {
carouselRef: ReturnType<typeof createEmblaCarousel>[0];
api: ReturnType<typeof createEmblaCarousel>[1];
scrollPrev: () => void;
scrollNext: () => void;
canScrollPrev: Accessor<boolean>;
canScrollNext: Accessor<boolean>;
} & CarouselProps;
const CarouselContext = createContext<Accessor<CarouselContextProps> | null>(
null,
);
const useCarousel = () => {
const context = useContext(CarouselContext);
if (!context) {
throw new Error("useCarousel must be used within a <Carousel />");
}
return context();
};
export const Carousel = (props: ComponentProps<"div"> & CarouselProps) => {
const merge = mergeProps<
ParentProps<ComponentProps<"div"> & CarouselProps>[]
>({ orientation: "horizontal" }, props);
const [local, rest] = splitProps(merge, [
"orientation",
"opts",
"setApi",
"plugins",
"class",
"children",
]);
const [carouselRef, api] = createEmblaCarousel(
() => ({
...local.opts,
axis: local.orientation === "horizontal" ? "x" : "y",
}),
() => (local.plugins === undefined ? [] : local.plugins),
);
const [canScrollPrev, setCanScrollPrev] = createSignal(false);
const [canScrollNext, setCanScrollNext] = createSignal(false);
const onSelect = (api: NonNullable<ReturnType<CarouselApi>>) => {
setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext());
};
const scrollPrev = () => api()?.scrollPrev();
const scrollNext = () => api()?.scrollNext();
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "ArrowLeft") {
event.preventDefault();
scrollPrev();
} else if (event.key === "ArrowRight") {
event.preventDefault();
scrollNext();
}
};
createEffect(() => {
if (!api() || !local.setApi) return;
local.setApi(api);
});
createEffect(() => {
const _api = api();
if (_api === undefined) return;
onSelect(_api);
_api.on("reInit", onSelect);
_api.on("select", onSelect);
onCleanup(() => {
_api.off("select", onSelect);
});
});
const value = createMemo(
() =>
({
carouselRef,
api,
opts: local.opts,
orientation:
local.orientation ||
(local.opts?.axis === "y" ? "vertical" : "horizontal"),
scrollPrev,
scrollNext,
canScrollPrev,
canScrollNext,
}) satisfies CarouselContextProps,
);
return (
<CarouselContext.Provider value={value}>
<div
onKeyDown={handleKeyDown}
class={cn("relative", local.class)}
role="region"
aria-roledescription="carousel"
{...rest}
>
{local.children}
</div>
</CarouselContext.Provider>
);
};
export const CarouselContent = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
const { carouselRef, orientation } = useCarousel();
return (
<div ref={carouselRef} class="overflow-hidden">
<div
class={cn(
"flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
local.class,
)}
{...rest}
/>
</div>
);
};
export const CarouselItem = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
const { orientation } = useCarousel();
return (
<div
role="group"
aria-roledescription="slide"
class={cn(
"min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4",
local.class,
)}
{...rest}
/>
);
};
export const CarouselPrevious = (
props: VoidProps<ComponentProps<typeof Button>>,
) => {
const merge = mergeProps<VoidProps<ComponentProps<typeof Button>[]>>(
{ variant: "outline", size: "icon" },
props,
);
const [local, rest] = splitProps(merge, ["class", "variant", "size"]);
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
return (
<Button
variant={local.variant}
size={local.size}
class={cn(
"absolute h-8 w-8 touch-manipulation rounded-full",
orientation === "horizontal"
? "-left-12 top-1/2 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
local.class,
)}
disabled={!canScrollPrev()}
onClick={scrollPrev}
{...rest}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="size-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 12h14M5 12l6 6m-6-6l6-6"
/>
<title>Previous slide</title>
</svg>
</Button>
);
};
export const CarouselNext = (
props: VoidProps<ComponentProps<typeof Button>>,
) => {
const merge = mergeProps<VoidProps<ComponentProps<typeof Button>[]>>(
{ variant: "outline", size: "icon" },
props,
);
const [local, rest] = splitProps(merge, ["class", "variant", "size"]);
const { orientation, scrollNext, canScrollNext } = useCarousel();
return (
<Button
variant={local.variant}
size={local.size}
class={cn(
"absolute h-8 w-8 touch-manipulation rounded-full",
orientation === "horizontal"
? "-right-12 top-1/2 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
local.class,
)}
disabled={!canScrollNext()}
onClick={scrollNext}
{...rest}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="size-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 12h14m-4 4l4-4m-4-4l4 4"
/>
<title>Next slide</title>
</svg>
</Button>
);
};

View file

@ -1,55 +1,104 @@
import * as React from "react";
import { cn } from "../libs/cn";
import type { CheckboxControlProps } from "@kobalte/core/checkbox";
import { Checkbox as CheckboxPrimitive } from "@kobalte/core/checkbox";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ValidComponent, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
export const CheckboxLabel = CheckboxPrimitive.Label;
export const Checkbox = CheckboxPrimitive;
export const CheckboxErrorMessage = CheckboxPrimitive.ErrorMessage;
export const CheckboxDescription = CheckboxPrimitive.Description;
type CheckboxContextValue = {
checked: boolean;
disabled?: boolean;
};
type checkboxControlProps<T extends ValidComponent = "div"> = VoidProps<
CheckboxControlProps<T> & { class?: string }
>;
const CheckboxContext = React.createContext<CheckboxContextValue | null>(null);
export const CheckboxControl = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, checkboxControlProps<T>>,
) => {
const [local, rest] = splitProps(props as checkboxControlProps, [
"class",
"children",
]);
export type CheckboxProps = {
checked: boolean;
onChange: (checked: boolean) => void;
disabled?: boolean;
className?: string;
children?: React.ReactNode;
};
export function Checkbox({
checked,
onChange,
disabled,
className,
children,
}: CheckboxProps) {
return (
<CheckboxContext.Provider value={{ checked, disabled }}>
<label className={cn("inline-flex items-center", className)}>
<input
type="checkbox"
className="sr-only peer"
checked={checked}
disabled={disabled}
onChange={(event) => onChange(event.currentTarget.checked)}
/>
{children}
</label>
</CheckboxContext.Provider>
);
}
export function CheckboxControl({
className,
}: {
className?: string;
}): React.ReactElement {
const context = React.useContext(CheckboxContext);
return (
<>
<CheckboxPrimitive.Input class="[&:focus-visible+div]:outline-none [&:focus-visible+div]:ring-[1.5px] [&:focus-visible+div]:ring-ring [&:focus-visible+div]:ring-offset-2 [&:focus-visible+div]:ring-offset-background" />
<CheckboxPrimitive.Control
class={cn(
"h-4 w-4 shrink-0 rounded-sm border border-primary shadow transition-shadow focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring data-[disabled]:cursor-not-allowed data-[checked]:bg-primary data-[checked]:text-primary-foreground data-[disabled]:opacity-50",
local.class,
<span
className={cn(
"h-4 w-4 shrink-0 rounded-sm border border-primary shadow transition-shadow peer-focus-visible:ring-[1.5px] peer-focus-visible:ring-ring peer-disabled:cursor-not-allowed",
context?.checked && "bg-primary text-primary-foreground",
context?.disabled && "opacity-50",
className,
)}
{...rest}
>
<CheckboxPrimitive.Indicator class="flex items-center justify-center text-current">
{context?.checked ? (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
className="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="m5 12l5 5L20 7"
/>
<title>Checkbox</title>
</svg>
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Control>
</>
) : null}
</span>
);
};
}
export function CheckboxLabel({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
className={cn("text-sm leading-none font-medium", className)}
{...props}
/>
);
}
export function CheckboxErrorMessage({
className,
...props
}: React.ComponentProps<"p">) {
return <p className={cn("text-sm text-destructive", className)} {...props} />;
}
export function CheckboxDescription({
className,
...props
}: React.ComponentProps<"p">) {
return (
<p className={cn("text-sm text-muted-foreground", className)} {...props} />
);
}

View file

@ -1,31 +0,0 @@
import { cn } from "../libs/cn";
import type { CollapsibleContentProps } from "@kobalte/core/collapsible";
import { Collapsible as CollapsiblePrimitive } from "@kobalte/core/collapsible";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
export const Collapsible = CollapsiblePrimitive;
export const CollapsibleTrigger = CollapsiblePrimitive.Trigger;
type collapsibleContentProps<T extends ValidComponent = "div"> =
CollapsibleContentProps<T> & {
class?: string;
};
export const CollapsibleContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, collapsibleContentProps<T>>,
) => {
const [local, rest] = splitProps(props as collapsibleContentProps, ["class"]);
return (
<CollapsiblePrimitive.Content
class={cn(
"animate-collapsible-up data-[expanded]:animate-collapsible-down",
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,156 +0,0 @@
import { cn } from "../libs/cn";
import type {
ComboboxContentProps,
ComboboxInputProps,
ComboboxItemProps,
ComboboxTriggerProps,
} from "@kobalte/core/combobox";
import { Combobox as ComboboxPrimitive } from "@kobalte/core/combobox";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ParentProps, ValidComponent, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
export const Combobox = ComboboxPrimitive;
export const ComboboxDescription = ComboboxPrimitive.Description;
export const ComboboxErrorMessage = ComboboxPrimitive.ErrorMessage;
export const ComboboxItemDescription = ComboboxPrimitive.ItemDescription;
export const ComboboxHiddenSelect = ComboboxPrimitive.HiddenSelect;
type comboboxInputProps<T extends ValidComponent = "input"> = VoidProps<
ComboboxInputProps<T> & {
class?: string;
}
>;
export const ComboboxInput = <T extends ValidComponent = "input">(
props: PolymorphicProps<T, comboboxInputProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxInputProps, ["class"]);
return (
<ComboboxPrimitive.Input
class={cn(
"h-full bg-transparent text-sm placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-50",
local.class,
)}
{...rest}
/>
);
};
type comboboxTriggerProps<T extends ValidComponent = "button"> = ParentProps<
ComboboxTriggerProps<T> & {
class?: string;
}
>;
export const ComboboxTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, comboboxTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxTriggerProps, [
"class",
"children",
]);
return (
<ComboboxPrimitive.Control>
<ComboboxPrimitive.Trigger
class={cn(
"flex h-9 w-full items-center justify-between rounded-md border border-input px-3 shadow-sm",
local.class,
)}
{...rest}
>
{local.children}
<ComboboxPrimitive.Icon class="flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4 opacity-50"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m8 9l4-4l4 4m0 6l-4 4l-4-4"
/>
<title>Arrow</title>
</svg>
</ComboboxPrimitive.Icon>
</ComboboxPrimitive.Trigger>
</ComboboxPrimitive.Control>
);
};
type comboboxContentProps<T extends ValidComponent = "div"> =
ComboboxContentProps<T> & {
class?: string;
};
export const ComboboxContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, comboboxContentProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxContentProps, ["class"]);
return (
<ComboboxPrimitive.Portal>
<ComboboxPrimitive.Content
class={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95 origin-[--kb-combobox-content-transform-origin]",
local.class,
)}
{...rest}
>
<ComboboxPrimitive.Listbox class="p-1" />
</ComboboxPrimitive.Content>
</ComboboxPrimitive.Portal>
);
};
type comboboxItemProps<T extends ValidComponent = "li"> = ParentProps<
ComboboxItemProps<T> & {
class?: string;
}
>;
export const ComboboxItem = <T extends ValidComponent = "li">(
props: PolymorphicProps<T, comboboxItemProps<T>>,
) => {
const [local, rest] = splitProps(props as comboboxItemProps, [
"class",
"children",
]);
return (
<ComboboxPrimitive.Item
class={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
local.class,
)}
{...rest}
>
<ComboboxPrimitive.ItemIndicator class="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m5 12l5 5L20 7"
/>
<title>Checked</title>
</svg>
</ComboboxPrimitive.ItemIndicator>
<ComboboxPrimitive.ItemLabel>
{local.children}
</ComboboxPrimitive.ItemLabel>
</ComboboxPrimitive.Item>
);
};

View file

@ -1,151 +0,0 @@
import { cn } from "../libs/cn";
import type {
CommandDialogProps,
CommandEmptyProps,
CommandGroupProps,
CommandInputProps,
CommandItemProps,
CommandListProps,
CommandRootProps,
} from "cmdk-solid";
import { Command as CommandPrimitive } from "cmdk-solid";
import type { ComponentProps, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
import { Dialog, DialogContent } from "./dialog";
export const Command = (props: CommandRootProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive
class={cn(
"flex size-full flex-col overflow-hidden bg-popover text-popover-foreground",
local.class,
)}
{...rest}
/>
);
};
export const CommandList = (props: CommandListProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.List
class={cn(
"max-h-[300px] overflow-y-auto overflow-x-hidden p-1",
local.class,
)}
{...rest}
/>
);
};
export const CommandInput = (props: VoidProps<CommandInputProps>) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div class="flex items-center border-b px-3" cmdk-input-wrapper="">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="mr-2 h-4 w-4 shrink-0 opacity-50"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 10a7 7 0 1 0 14 0a7 7 0 1 0-14 0m18 11l-6-6"
/>
<title>Search</title>
</svg>
<CommandPrimitive.Input
class={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
local.class,
)}
{...rest}
/>
</div>
);
};
export const CommandItem = (props: CommandItemProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-selected:bg-accent aria-selected:text-accent-foreground",
local.class,
)}
{...rest}
/>
);
};
export const CommandShortcut = (props: ComponentProps<"span">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<span
class={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
local.class,
)}
{...rest}
/>
);
};
export const CommandDialog = (props: CommandDialogProps) => {
const [local, rest] = splitProps(props, ["children"]);
return (
<Dialog {...rest}>
<DialogContent class="overflow-hidden p-0">
<Command class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:size-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:size-5">
{local.children}
</Command>
</DialogContent>
</Dialog>
);
};
export const CommandEmpty = (props: CommandEmptyProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Empty
class={cn("py-6 text-center text-sm", local.class)}
{...rest}
/>
);
};
export const CommandGroup = (props: CommandGroupProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Group
class={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
local.class,
)}
{...rest}
/>
);
};
export const CommandSeparator = (props: CommandEmptyProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Separator
class={cn("-mx-1 h-px bg-border", local.class)}
{...rest}
/>
);
};

View file

@ -1,327 +1,182 @@
import * as React from "react";
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
import { cn } from "../libs/cn";
import type {
ContextMenuCheckboxItemProps,
ContextMenuContentProps,
ContextMenuGroupLabelProps,
ContextMenuItemLabelProps,
ContextMenuItemProps,
ContextMenuRadioItemProps,
ContextMenuSeparatorProps,
ContextMenuSubContentProps,
ContextMenuSubTriggerProps,
} from "@kobalte/core/context-menu";
import { ContextMenu as ContextMenuPrimitive } from "@kobalte/core/context-menu";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type {
ComponentProps,
ParentProps,
ValidComponent,
VoidProps,
} from "solid-js";
import { splitProps } from "solid-js";
export const ContextMenu = ContextMenuPrimitive;
export const ContextMenu = ContextMenuPrimitive.Root;
export const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
export const ContextMenuGroup = ContextMenuPrimitive.Group;
export const ContextMenuSub = ContextMenuPrimitive.Sub;
export const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
type contextMenuSubTriggerProps<T extends ValidComponent = "div"> = ParentProps<
ContextMenuSubTriggerProps<T> & {
class?: string;
export const ContextMenuSubTrigger = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
inset?: boolean;
}
>;
export const ContextMenuSubTrigger = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, contextMenuSubTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as contextMenuSubTriggerProps, [
"class",
"children",
"inset",
]);
return (
>(({ className, inset, children, ...props }, ref) => (
<ContextMenuPrimitive.SubTrigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[expanded]:bg-accent data-[expanded]:text-accent-foreground",
local.inset && "pl-8",
local.class,
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground",
inset && "pl-8",
className,
)}
{...rest}
{...props}
>
{local.children}
<svg
xmlns="http://www.w3.org/2000/svg"
class="ml-auto h-4 w-4"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m9 6l6 6l-6 6"
/>
<title>Arrow</title>
</svg>
{children}
</ContextMenuPrimitive.SubTrigger>
);
};
));
type contextMenuSubContentProps<T extends ValidComponent = "div"> =
ContextMenuSubContentProps<T> & {
class?: string;
};
ContextMenuSubTrigger.displayName = "ContextMenuSubTrigger";
export const ContextMenuSubContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, contextMenuSubContentProps<T>>,
) => {
const [local, rest] = splitProps(props as contextMenuSubContentProps, [
"class",
]);
return (
export const ContextMenuSubContent = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.SubContent
class={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class,
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg",
className,
)}
{...rest}
{...props}
/>
</ContextMenuPrimitive.Portal>
);
};
));
type contextMenuContentProps<T extends ValidComponent = "div"> =
ContextMenuContentProps<T> & {
class?: string;
};
ContextMenuSubContent.displayName = "ContextMenuSubContent";
export const ContextMenuContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, contextMenuContentProps<T>>,
) => {
const [local, rest] = splitProps(props as contextMenuContentProps, ["class"]);
return (
export const ContextMenuContent = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Content
class={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md transition-shadow focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class,
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
className,
)}
{...rest}
{...props}
/>
</ContextMenuPrimitive.Portal>
);
};
));
type contextMenuItemProps<T extends ValidComponent = "div"> =
ContextMenuItemProps<T> & {
class?: string;
ContextMenuContent.displayName = "ContextMenuContent";
export const ContextMenuItem = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
inset?: boolean;
};
export const ContextMenuItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, contextMenuItemProps<T>>,
) => {
const [local, rest] = splitProps(props as contextMenuItemProps, [
"class",
"inset",
]);
return (
}
>(({ className, inset, ...props }, ref) => (
<ContextMenuPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.inset && "pl-8",
local.class,
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground",
inset && "pl-8",
className,
)}
{...rest}
{...props}
/>
);
};
));
type contextMenuCheckboxItemProps<T extends ValidComponent = "div"> =
ParentProps<
ContextMenuCheckboxItemProps<T> & {
class?: string;
}
>;
ContextMenuItem.displayName = "ContextMenuItem";
export const ContextMenuCheckboxItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, contextMenuCheckboxItemProps<T>>,
) => {
const [local, rest] = splitProps(props as contextMenuCheckboxItemProps, [
"class",
"children",
]);
return (
export const ContextMenuCheckboxItem = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
>(({ className, children, ...props }, ref) => (
<ContextMenuPrimitive.CheckboxItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class,
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground",
className,
)}
{...rest}
{...props}
>
<ContextMenuPrimitive.ItemIndicator class="absolute left-2 inline-flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m5 12l5 5L20 7"
/>
<title>Checkbox</title>
</svg>
</ContextMenuPrimitive.ItemIndicator>
{local.children}
{children}
</ContextMenuPrimitive.CheckboxItem>
);
};
));
type contextMenuRadioItemProps<T extends ValidComponent = "div"> = ParentProps<
ContextMenuRadioItemProps<T> & {
class?: string;
}
>;
ContextMenuCheckboxItem.displayName = "ContextMenuCheckboxItem";
export const ContextMenuRadioItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, contextMenuRadioItemProps<T>>,
) => {
const [local, rest] = splitProps(props as contextMenuRadioItemProps, [
"class",
"children",
]);
return (
export const ContextMenuRadioItem = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<ContextMenuPrimitive.RadioItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class,
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground",
className,
)}
{...rest}
{...props}
>
<ContextMenuPrimitive.ItemIndicator class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-2 w-2"
>
<g
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
>
<path d="M0 0h24v24H0z" />
<path
fill="currentColor"
d="M7 3.34a10 10 0 1 1-4.995 8.984L2 12l.005-.324A10 10 0 0 1 7 3.34"
/>
</g>
<title>Radio</title>
</svg>
</ContextMenuPrimitive.ItemIndicator>
{local.children}
{children}
</ContextMenuPrimitive.RadioItem>
);
};
));
type contextMenuItemLabelProps<T extends ValidComponent = "div"> =
ContextMenuItemLabelProps<T> & {
class?: string;
ContextMenuRadioItem.displayName = "ContextMenuRadioItem";
export const ContextMenuItemLabel = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
inset?: boolean;
};
export const ContextMenuItemLabel = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, contextMenuItemLabelProps<T>>,
) => {
const [local, rest] = splitProps(props as contextMenuItemLabelProps, [
"class",
"inset",
]);
return (
<ContextMenuPrimitive.ItemLabel
class={cn(
"px-2 py-1.5 text-sm font-semibold text-foreground",
local.inset && "pl-8",
local.class,
)}
{...rest}
/>
);
};
type contextMenuGroupLabelProps<T extends ValidComponent = "span"> =
ContextMenuGroupLabelProps<T> & {
class?: string;
inset?: boolean;
};
export const ContextMenuGroupLabel = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, contextMenuGroupLabelProps<T>>,
) => {
const [local, rest] = splitProps(props as contextMenuGroupLabelProps, [
"class",
"inset",
]);
return (
<ContextMenuPrimitive.GroupLabel
as="div"
class={cn(
"px-2 py-1.5 text-sm font-semibold text-foreground",
local.inset && "pl-8",
local.class,
)}
{...rest}
/>
);
};
type contextMenuSeparatorProps<T extends ValidComponent = "hr"> = VoidProps<
ContextMenuSeparatorProps<T> & {
class?: string;
}
>;
export const ContextMenuSeparator = <T extends ValidComponent = "hr">(
props: PolymorphicProps<T, contextMenuSeparatorProps<T>>,
) => {
const [local, rest] = splitProps(props as contextMenuSeparatorProps, [
"class",
]);
return (
<ContextMenuPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-border", local.class)}
{...rest}
/>
);
};
export const ContextMenuShortcut = (props: ComponentProps<"span">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<span
class={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
local.class,
>(({ className, inset, ...props }, ref) => (
<ContextMenuPrimitive.Label
ref={ref}
className={cn(
"px-2 py-1.5 text-sm font-semibold text-foreground",
inset && "pl-8",
className,
)}
{...rest}
{...props}
/>
));
ContextMenuItemLabel.displayName = "ContextMenuItemLabel";
export const ContextMenuGroupLabel = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<ContextMenuPrimitive.Label
ref={ref}
className={cn(
"px-2 py-1.5 text-sm font-semibold text-foreground",
inset && "pl-8",
className,
)}
{...props}
/>
));
ContextMenuGroupLabel.displayName = "ContextMenuGroupLabel";
export const ContextMenuSeparator = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-border", className)}
{...props}
/>
));
ContextMenuSeparator.displayName = "ContextMenuSeparator";
export const ContextMenuShortcut = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}
{...props}
/>
);
};

View file

@ -1,128 +0,0 @@
import { cn } from "../libs/cn";
import type {
DialogContentProps,
DialogDescriptionProps,
DialogTitleProps,
} from "@kobalte/core/dialog";
import { Dialog as DialogPrimitive } from "@kobalte/core/dialog";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ComponentProps, ParentProps, ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
export const Dialog = DialogPrimitive;
export const DialogTrigger = DialogPrimitive.Trigger;
type dialogContentProps<T extends ValidComponent = "div"> = ParentProps<
DialogContentProps<T> & {
class?: string;
}
>;
export const DialogContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, dialogContentProps<T>>,
) => {
const [local, rest] = splitProps(props as dialogContentProps, [
"class",
"children",
]);
return (
<DialogPrimitive.Portal>
<DialogPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 bg-background/80 data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0",
)}
{...rest}
/>
<DialogPrimitive.Content
class={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg data-[closed]:duration-200 data-[expanded]:duration-200 data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95 data-[closed]:slide-out-to-left-1/2 data-[closed]:slide-out-to-top-[48%] data-[expanded]:slide-in-from-left-1/2 data-[expanded]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
local.class,
)}
{...rest}
>
{local.children}
<DialogPrimitive.CloseButton class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-[opacity,box-shadow] hover:opacity-100 focus:outline-none focus:ring-[1.5px] focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M18 6L6 18M6 6l12 12"
/>
<title>Close</title>
</svg>
</DialogPrimitive.CloseButton>
</DialogPrimitive.Content>
</DialogPrimitive.Portal>
);
};
type dialogTitleProps<T extends ValidComponent = "h2"> = DialogTitleProps<T> & {
class?: string;
};
export const DialogTitle = <T extends ValidComponent = "h2">(
props: PolymorphicProps<T, dialogTitleProps<T>>,
) => {
const [local, rest] = splitProps(props as dialogTitleProps, ["class"]);
return (
<DialogPrimitive.Title
class={cn("text-lg font-semibold text-foreground", local.class)}
{...rest}
/>
);
};
type dialogDescriptionProps<T extends ValidComponent = "p"> =
DialogDescriptionProps<T> & {
class?: string;
};
export const DialogDescription = <T extends ValidComponent = "p">(
props: PolymorphicProps<T, dialogDescriptionProps<T>>,
) => {
const [local, rest] = splitProps(props as dialogDescriptionProps, ["class"]);
return (
<DialogPrimitive.Description
class={cn("text-sm text-muted-foreground", local.class)}
{...rest}
/>
);
};
export const DialogHeader = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div
class={cn(
"flex flex-col space-y-2 text-center sm:text-left",
local.class,
)}
{...rest}
/>
);
};
export const DialogFooter = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div
class={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,107 +0,0 @@
import { cn } from "../libs/cn";
import type {
ContentProps,
DescriptionProps,
DynamicProps,
LabelProps,
} from "@corvu/drawer";
import DrawerPrimitive from "@corvu/drawer";
import type { ComponentProps, ParentProps, ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
export const Drawer = DrawerPrimitive;
export const DrawerTrigger = DrawerPrimitive.Trigger;
export const DrawerClose = DrawerPrimitive.Close;
type drawerContentProps<T extends ValidComponent = "div"> = ParentProps<
ContentProps<T> & {
class?: string;
}
>;
export const DrawerContent = <T extends ValidComponent = "div">(
props: DynamicProps<T, drawerContentProps<T>>,
) => {
const [local, rest] = splitProps(props as drawerContentProps, [
"class",
"children",
]);
const ctx = DrawerPrimitive.useContext();
return (
<DrawerPrimitive.Portal>
<DrawerPrimitive.Overlay
class="fixed inset-0 z-50 data-[transitioning]:transition-colors data-[transitioning]:duration-200"
style={{
"background-color": `hsl(var(--background) / ${0.8 * ctx.openPercentage()})`,
}}
/>
<DrawerPrimitive.Content
class={cn(
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-xl border bg-background after:absolute after:inset-x-0 after:top-full after:h-[50%] after:bg-inherit data-[transitioning]:transition-transform data-[transitioning]:duration-200 md:select-none",
local.class,
)}
{...rest}
>
<div class="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
{local.children}
</DrawerPrimitive.Content>
</DrawerPrimitive.Portal>
);
};
export const DrawerHeader = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div
class={cn("grid gap-1.5 p-4 text-center sm:text-left", local.class)}
{...rest}
/>
);
};
export const DrawerFooter = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div class={cn("mt-auto flex flex-col gap-2 p-4", local.class)} {...rest} />
);
};
type DrawerLabelProps = LabelProps & {
class?: string;
};
export const DrawerLabel = <T extends ValidComponent = "h2">(
props: DynamicProps<T, DrawerLabelProps>,
) => {
const [local, rest] = splitProps(props as DrawerLabelProps, ["class"]);
return (
<DrawerPrimitive.Label
class={cn(
"text-lg font-semibold leading-none tracking-tight",
local.class,
)}
{...rest}
/>
);
};
type DrawerDescriptionProps = DescriptionProps & {
class?: string;
};
export const DrawerDescription = <T extends ValidComponent = "p">(
props: DynamicProps<T, DrawerDescriptionProps>,
) => {
const [local, rest] = splitProps(props as DrawerDescriptionProps, ["class"]);
return (
<DrawerPrimitive.Description
class={cn("text-sm text-muted-foreground", local.class)}
{...rest}
/>
);
};

View file

@ -1,320 +0,0 @@
import { cn } from "../libs/cn";
import type {
DropdownMenuCheckboxItemProps,
DropdownMenuContentProps,
DropdownMenuGroupLabelProps,
DropdownMenuItemLabelProps,
DropdownMenuItemProps,
DropdownMenuRadioItemProps,
DropdownMenuRootProps,
DropdownMenuSeparatorProps,
DropdownMenuSubTriggerProps,
} from "@kobalte/core/dropdown-menu";
import { DropdownMenu as DropdownMenuPrimitive } from "@kobalte/core/dropdown-menu";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ComponentProps, ParentProps, ValidComponent } from "solid-js";
import { mergeProps, splitProps } from "solid-js";
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
export const DropdownMenuSub = DropdownMenuPrimitive.Sub;
export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
export const DropdownMenu = (props: DropdownMenuRootProps) => {
const merge = mergeProps<DropdownMenuRootProps[]>(
{
gutter: 4,
flip: false,
},
props,
);
return <DropdownMenuPrimitive {...merge} />;
};
type dropdownMenuContentProps<T extends ValidComponent = "div"> =
DropdownMenuContentProps<T> & {
class?: string;
};
export const DropdownMenuContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, dropdownMenuContentProps<T>>,
) => {
const [local, rest] = splitProps(props as dropdownMenuContentProps, [
"class",
]);
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
class={cn(
"min-w-8rem z-50 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md transition-shadow focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class,
)}
{...rest}
/>
</DropdownMenuPrimitive.Portal>
);
};
type dropdownMenuItemProps<T extends ValidComponent = "div"> =
DropdownMenuItemProps<T> & {
class?: string;
inset?: boolean;
};
export const DropdownMenuItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, dropdownMenuItemProps<T>>,
) => {
const [local, rest] = splitProps(props as dropdownMenuItemProps, [
"class",
"inset",
]);
return (
<DropdownMenuPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.inset && "pl-8",
local.class,
)}
{...rest}
/>
);
};
type dropdownMenuGroupLabelProps<T extends ValidComponent = "span"> =
DropdownMenuGroupLabelProps<T> & {
class?: string;
};
export const DropdownMenuGroupLabel = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, dropdownMenuGroupLabelProps<T>>,
) => {
const [local, rest] = splitProps(props as dropdownMenuGroupLabelProps, [
"class",
]);
return (
<DropdownMenuPrimitive.GroupLabel
as="div"
class={cn("px-2 py-1.5 text-sm font-semibold", local.class)}
{...rest}
/>
);
};
type dropdownMenuItemLabelProps<T extends ValidComponent = "div"> =
DropdownMenuItemLabelProps<T> & {
class?: string;
};
export const DropdownMenuItemLabel = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, dropdownMenuItemLabelProps<T>>,
) => {
const [local, rest] = splitProps(props as dropdownMenuItemLabelProps, [
"class",
]);
return (
<DropdownMenuPrimitive.ItemLabel
as="div"
class={cn("px-2 py-1.5 text-sm font-semibold", local.class)}
{...rest}
/>
);
};
type dropdownMenuSeparatorProps<T extends ValidComponent = "hr"> =
DropdownMenuSeparatorProps<T> & {
class?: string;
};
export const DropdownMenuSeparator = <T extends ValidComponent = "hr">(
props: PolymorphicProps<T, dropdownMenuSeparatorProps<T>>,
) => {
const [local, rest] = splitProps(props as dropdownMenuSeparatorProps, [
"class",
]);
return (
<DropdownMenuPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", local.class)}
{...rest}
/>
);
};
export const DropdownMenuShortcut = (props: ComponentProps<"span">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<span
class={cn("ml-auto text-xs tracking-widest opacity-60", local.class)}
{...rest}
/>
);
};
type dropdownMenuSubTriggerProps<T extends ValidComponent = "div"> =
ParentProps<
DropdownMenuSubTriggerProps<T> & {
class?: string;
}
>;
export const DropdownMenuSubTrigger = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, dropdownMenuSubTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as dropdownMenuSubTriggerProps, [
"class",
"children",
]);
return (
<DropdownMenuPrimitive.SubTrigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[expanded]:bg-accent",
local.class,
)}
{...rest}
>
{local.children}
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
class="ml-auto h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m9 6l6 6l-6 6"
/>
<title>Arrow</title>
</svg>
</DropdownMenuPrimitive.SubTrigger>
);
};
type dropdownMenuSubContentProps<T extends ValidComponent = "div"> =
DropdownMenuSubTriggerProps<T> & {
class?: string;
};
export const DropdownMenuSubContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, dropdownMenuSubContentProps<T>>,
) => {
const [local, rest] = splitProps(props as dropdownMenuSubContentProps, [
"class",
]);
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.SubContent
class={cn(
"min-w-8rem z-50 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class,
)}
{...rest}
/>
</DropdownMenuPrimitive.Portal>
);
};
type dropdownMenuCheckboxItemProps<T extends ValidComponent = "div"> =
ParentProps<
DropdownMenuCheckboxItemProps<T> & {
class?: string;
}
>;
export const DropdownMenuCheckboxItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, dropdownMenuCheckboxItemProps<T>>,
) => {
const [local, rest] = splitProps(props as dropdownMenuCheckboxItemProps, [
"class",
"children",
]);
return (
<DropdownMenuPrimitive.CheckboxItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class,
)}
{...rest}
>
<DropdownMenuPrimitive.ItemIndicator class="absolute left-2 inline-flex h-4 w-4 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m5 12l5 5L20 7"
/>
<title>Checkbox</title>
</svg>
</DropdownMenuPrimitive.ItemIndicator>
{props.children}
</DropdownMenuPrimitive.CheckboxItem>
);
};
type dropdownMenuRadioItemProps<T extends ValidComponent = "div"> = ParentProps<
DropdownMenuRadioItemProps<T> & {
class?: string;
}
>;
export const DropdownMenuRadioItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, dropdownMenuRadioItemProps<T>>,
) => {
const [local, rest] = splitProps(props as dropdownMenuRadioItemProps, [
"class",
"children",
]);
return (
<DropdownMenuPrimitive.RadioItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class,
)}
{...rest}
>
<DropdownMenuPrimitive.ItemIndicator class="absolute left-2 inline-flex h-4 w-4 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-2 w-2"
>
<g
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
>
<path d="M0 0h24v24H0z" />
<path
fill="currentColor"
d="M7 3.34a10 10 0 1 1-4.995 8.984L2 12l.005-.324A10 10 0 0 1 7 3.34"
/>
</g>
<title>Radio</title>
</svg>
</DropdownMenuPrimitive.ItemIndicator>
{props.children}
</DropdownMenuPrimitive.RadioItem>
);
};

View file

@ -1,32 +0,0 @@
import { cn } from "../libs/cn";
import type { HoverCardContentProps } from "@kobalte/core/hover-card";
import { HoverCard as HoverCardPrimitive } from "@kobalte/core/hover-card";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
export const HoverCard = HoverCardPrimitive;
export const HoverCardTrigger = HoverCardPrimitive.Trigger;
type hoverCardContentProps<T extends ValidComponent = "div"> =
HoverCardContentProps<T> & {
class?: string;
};
export const HoverCardContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, hoverCardContentProps<T>>,
) => {
const [local, rest] = splitProps(props as hoverCardContentProps, ["class"]);
return (
<HoverCardPrimitive.Portal>
<HoverCardPrimitive.Content
class={cn(
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class,
)}
{...rest}
/>
</HoverCardPrimitive.Portal>
);
};

View file

@ -1,68 +0,0 @@
import { cn } from "../libs/cn";
import type {
ImageFallbackProps,
ImageImgProps,
ImageRootProps,
} from "@kobalte/core/image";
import { Image as ImagePrimitive } from "@kobalte/core/image";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
type imageRootProps<T extends ValidComponent = "span"> = ImageRootProps<T> & {
class?: string;
};
export const ImageRoot = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, imageRootProps<T>>,
) => {
const [local, rest] = splitProps(props as imageRootProps, ["class"]);
return (
<ImagePrimitive
class={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
local.class,
)}
{...rest}
/>
);
};
type imageProps<T extends ValidComponent = "img"> = ImageImgProps<T> & {
class?: string;
};
export const Image = <T extends ValidComponent = "img">(
props: PolymorphicProps<T, imageProps<T>>,
) => {
const [local, rest] = splitProps(props as imageProps, ["class"]);
return (
<ImagePrimitive.Img
class={cn("aspect-square h-full w-full", local.class)}
{...rest}
/>
);
};
type imageFallbackProps<T extends ValidComponent = "span"> =
ImageFallbackProps<T> & {
class?: string;
};
export const ImageFallback = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, imageFallbackProps<T>>,
) => {
const [local, rest] = splitProps(props as imageFallbackProps, ["class"]);
return (
<ImagePrimitive.Fallback
class={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,27 +1,30 @@
import { splitProps, type JSX } from "solid-js";
import * as React from "react";
import { cn } from "../libs/cn";
interface InputProps extends JSX.HTMLAttributes<HTMLInputElement> {
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
type?: string;
}
function Input(props: InputProps) {
const [local, others] = splitProps(props, ["class", "type"]);
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={local.type}
ref={ref}
type={type}
data-slot="input"
class={cn(
className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base text-foreground shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-input focus-visible:dark:bg-input/50 transition-all duration-200 ease-in-out",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
local.class,
className,
)}
{...others}
{...props}
/>
);
}
},
);
Input.displayName = "Input";
export { Input };

View file

@ -1,20 +1,23 @@
import { cn } from "../libs/cn";
import type { Component, JSX } from "solid-js";
import { splitProps } from "solid-js";
const Label: Component<JSX.LabelHTMLAttributes<HTMLLabelElement>> = (props) => {
const [local, others] = splitProps(props, ["class"]);
import * as React from "react";
const Label = React.forwardRef<
HTMLLabelElement,
React.LabelHTMLAttributes<HTMLLabelElement>
>(({ className, ...props }, ref) => {
return (
<label
ref={ref}
data-slot="label"
class={cn(
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50 transition-all duration-200 ease-in-out",
local.class,
className,
)}
{...others}
{...props}
/>
);
};
});
Label.displayName = "Label";
export { Label };

View file

@ -1,356 +0,0 @@
import { cn } from "../libs/cn";
import type {
MenubarCheckboxItemProps,
MenubarContentProps,
MenubarItemLabelProps,
MenubarItemProps,
MenubarMenuProps,
MenubarRadioItemProps,
MenubarRootProps,
MenubarSeparatorProps,
MenubarSubContentProps,
MenubarSubTriggerProps,
MenubarTriggerProps,
} from "@kobalte/core/menubar";
import { Menubar as MenubarPrimitive } from "@kobalte/core/menubar";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ComponentProps, ParentProps, ValidComponent } from "solid-js";
import { mergeProps, splitProps } from "solid-js";
export const MenubarSub = MenubarPrimitive.Sub;
export const MenubarRadioGroup = MenubarPrimitive.RadioGroup;
type menubarProps<T extends ValidComponent = "div"> = MenubarRootProps<T> & {
class?: string;
};
export const Menubar = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, menubarProps<T>>,
) => {
const [local, rest] = splitProps(props as menubarProps, ["class"]);
return (
<MenubarPrimitive
class={cn(
"flex h-9 items-center space-x-1 rounded-md border bg-background p-1 shadow-sm",
local.class,
)}
{...rest}
/>
);
};
export const MenubarMenu = (props: MenubarMenuProps) => {
const merge = mergeProps<MenubarMenuProps[]>(
{
gutter: 8,
shift: -4,
flip: false,
},
props,
);
return <MenubarPrimitive.Menu {...merge} />;
};
type menubarTriggerProps<T extends ValidComponent = "button"> =
MenubarTriggerProps<T> & {
class?: string;
};
export const MenubarTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, menubarTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as menubarTriggerProps, ["class"]);
return (
<MenubarPrimitive.Trigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[expanded]:bg-accent data-[expanded]:text-accent-foreground",
local.class,
)}
{...rest}
/>
);
};
type menubarSubTriggerProps<T extends ValidComponent = "button"> = ParentProps<
MenubarSubTriggerProps<T> & {
class?: string;
inset?: boolean;
}
>;
export const MenubarSubTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, menubarSubTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as menubarSubTriggerProps, [
"class",
"children",
"inset",
]);
return (
<MenubarPrimitive.SubTrigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[expanded]:bg-accent data-[expanded]:text-accent-foreground",
local.inset && "pl-8",
local.class,
)}
{...rest}
>
{local.children}
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
class="ml-auto h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m9 6l6 6l-6 6"
/>
<title>Arrow</title>
</svg>
</MenubarPrimitive.SubTrigger>
);
};
type menubarSubContentProps<T extends ValidComponent = "div"> = ParentProps<
MenubarSubContentProps<T> & {
class?: string;
}
>;
export const MenubarSubContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, menubarSubContentProps<T>>,
) => {
const [local, rest] = splitProps(props as menubarSubContentProps, [
"class",
"children",
]);
return (
<MenubarPrimitive.Portal>
<MenubarPrimitive.SubContent
class={cn(
"z-50 min-w-[8rem] origin-[--kb-menu-content-transform-origin] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg outline-none data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class,
)}
{...rest}
>
{local.children}
</MenubarPrimitive.SubContent>
</MenubarPrimitive.Portal>
);
};
type menubarContentProps<T extends ValidComponent = "div"> = ParentProps<
MenubarContentProps<T> & {
class?: string;
}
>;
export const MenubarContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, menubarContentProps<T>>,
) => {
const [local, rest] = splitProps(props as menubarContentProps, [
"class",
"children",
]);
return (
<MenubarPrimitive.Portal>
<MenubarPrimitive.Content
class={cn(
"z-50 min-w-[12rem] origin-[--kb-menu-content-transform-origin] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none data-[expanded]:animate-in data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class,
)}
{...rest}
>
{local.children}
</MenubarPrimitive.Content>
</MenubarPrimitive.Portal>
);
};
type menubarItemProps<T extends ValidComponent = "div"> =
MenubarItemProps<T> & {
class?: string;
inset?: boolean;
};
export const MenubarItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, menubarItemProps<T>>,
) => {
const [local, rest] = splitProps(props as menubarItemProps, [
"class",
"inset",
]);
return (
<MenubarPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.inset && "pl-8",
local.class,
)}
{...rest}
/>
);
};
type menubarItemLabelProps<T extends ValidComponent = "div"> =
MenubarItemLabelProps<T> & {
class?: string;
inset?: boolean;
};
export const MenubarItemLabel = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, menubarItemLabelProps<T>>,
) => {
const [local, rest] = splitProps(props as menubarItemLabelProps, [
"class",
"inset",
]);
return (
<MenubarPrimitive.ItemLabel
class={cn(
"px-2 py-1.5 text-sm font-semibold",
local.inset && "pl-8",
local.class,
)}
{...rest}
/>
);
};
type menubarSeparatorProps<T extends ValidComponent = "hr"> =
MenubarSeparatorProps<T> & {
class?: string;
};
export const MenubarSeparator = <T extends ValidComponent = "hr">(
props: PolymorphicProps<T, menubarSeparatorProps<T>>,
) => {
const [local, rest] = splitProps(props as menubarSeparatorProps, ["class"]);
return (
<MenubarPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", local.class)}
{...rest}
/>
);
};
type menubarCheckboxItemProps<T extends ValidComponent = "div"> = ParentProps<
MenubarCheckboxItemProps<T> & {
class?: string;
}
>;
export const MenubarCheckboxItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, menubarCheckboxItemProps<T>>,
) => {
const [local, rest] = splitProps(props as menubarCheckboxItemProps, [
"class",
"children",
]);
return (
<MenubarPrimitive.CheckboxItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class,
)}
{...rest}
>
<MenubarPrimitive.ItemIndicator class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m5 12l5 5L20 7"
/>
<title>Checkbox</title>
</svg>
</MenubarPrimitive.ItemIndicator>
{local.children}
</MenubarPrimitive.CheckboxItem>
);
};
type menubarRadioItemProps<T extends ValidComponent = "div"> = ParentProps<
MenubarRadioItemProps<T> & {
class?: string;
}
>;
export const MenubarRadioItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, menubarRadioItemProps<T>>,
) => {
const [local, rest] = splitProps(props as menubarRadioItemProps, [
"class",
"children",
]);
return (
<MenubarPrimitive.RadioItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class,
)}
{...rest}
>
<MenubarPrimitive.ItemIndicator class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-2 w-2"
>
<g
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
>
<path d="M0 0h24v24H0z" />
<path
fill="currentColor"
d="M7 3.34a10 10 0 1 1-4.995 8.984L2 12l.005-.324A10 10 0 0 1 7 3.34"
/>
</g>
<title>Radio</title>
</svg>
</MenubarPrimitive.ItemIndicator>
{local.children}
</MenubarPrimitive.RadioItem>
);
};
export const MenubarShortcut = (props: ComponentProps<"span">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<span
class={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,169 +0,0 @@
import { cn } from "../libs/cn";
import type {
NavigationMenuContentProps,
NavigationMenuRootProps,
NavigationMenuTriggerProps,
} from "@kobalte/core/navigation-menu";
import { NavigationMenu as NavigationMenuPrimitive } from "@kobalte/core/navigation-menu";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import {
type ParentProps,
Show,
type ValidComponent,
mergeProps,
splitProps,
} from "solid-js";
export const NavigationMenuItem = NavigationMenuPrimitive.Menu;
export const NavigationMenuLink = NavigationMenuPrimitive.Item;
export const NavigationMenuItemLabel = NavigationMenuPrimitive.ItemLabel;
export const NavigationMenuDescription =
NavigationMenuPrimitive.ItemDescription;
export const NavigationMenuItemIndicator =
NavigationMenuPrimitive.ItemIndicator;
export const NavigationMenuSub = NavigationMenuPrimitive.Sub;
export const NavigationMenuSubTrigger = NavigationMenuPrimitive.SubTrigger;
export const NavigationMenuSubContent = NavigationMenuPrimitive.SubContent;
export const NavigationMenuRadioGroup = NavigationMenuPrimitive.RadioGroup;
export const NavigationMenuRadioItem = NavigationMenuPrimitive.RadioItem;
export const NavigationMenuCheckboxItem = NavigationMenuPrimitive.CheckboxItem;
export const NavigationMenuSeparator = NavigationMenuPrimitive.Separator;
type withArrow = {
withArrow?: boolean;
};
type navigationMenuProps<T extends ValidComponent = "ul"> = ParentProps<
NavigationMenuRootProps<T> &
withArrow & {
class?: string;
}
>;
export const NavigationMenu = <T extends ValidComponent = "ul">(
props: PolymorphicProps<T, navigationMenuProps<T>>,
) => {
const merge = mergeProps<navigationMenuProps<T>[]>(
{
get gutter() {
return props.withArrow ? props.gutter : 6;
},
withArrow: false,
flip: false,
},
props,
);
const [local, rest] = splitProps(merge as navigationMenuProps, [
"class",
"children",
"withArrow",
]);
return (
<NavigationMenuPrimitive
class={cn("flex w-max items-center justify-center gap-x-1", local.class)}
{...rest}
>
{local.children}
<NavigationMenuPrimitive.Viewport
class={cn(
"pointer-events-none z-50 overflow-x-clip overflow-y-visible rounded-md border bg-popover text-popover-foreground shadow",
"h-[--kb-navigation-menu-viewport-height] w-[--kb-navigation-menu-viewport-width] transition-[width,height] duration-300",
"origin-[--kb-menu-content-transform-origin]",
"data-[expanded]:duration-300 data-[expanded]:animate-in data-[expanded]:fade-in data-[expanded]:zoom-in-95",
"data-[closed]:duration-300 data-[closed]:animate-out data-[closed]:fade-out data-[closed]:zoom-out-95",
)}
>
<Show when={local.withArrow}>
<NavigationMenuPrimitive.Arrow class="transition-transform duration-300" />
</Show>
</NavigationMenuPrimitive.Viewport>
</NavigationMenuPrimitive>
);
};
type navigationMenuTriggerProps<T extends ValidComponent = "button"> =
ParentProps<
NavigationMenuTriggerProps<T> &
withArrow & {
class?: string;
}
>;
export const NavigationMenuTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, navigationMenuTriggerProps<T>>,
) => {
const merge = mergeProps<navigationMenuTriggerProps<T>[]>(
{
get withArrow() {
return props.as === undefined ? true : props.withArrow;
},
},
props,
);
const [local, rest] = splitProps(merge as navigationMenuTriggerProps, [
"class",
"children",
"withArrow",
]);
return (
<NavigationMenuPrimitive.Trigger
class={cn(
"inline-flex w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium outline-none transition-colors duration-300 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50",
local.class,
)}
{...rest}
>
{local.children}
<Show when={local.withArrow}>
<NavigationMenuPrimitive.Icon
class="ml-1 size-3 transition-transform duration-300 data-[expanded]:rotate-180"
as="svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m6 9l6 6l6-6"
/>
</NavigationMenuPrimitive.Icon>
</Show>
</NavigationMenuPrimitive.Trigger>
);
};
type navigationMenuContentProps<T extends ValidComponent = "ul"> = ParentProps<
NavigationMenuContentProps<T> & {
class?: string;
}
>;
export const NavigationMenuContent = <T extends ValidComponent = "ul">(
props: PolymorphicProps<T, navigationMenuContentProps<T>>,
) => {
const [local, rest] = splitProps(props as navigationMenuContentProps, [
"class",
"children",
]);
return (
<NavigationMenuPrimitive.Portal>
<NavigationMenuPrimitive.Content
class={cn(
"absolute left-0 top-0 p-4 outline-none",
"data-[motion^=from-]:duration-300 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52",
"data-[motion^=to-]:duration-300 data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52",
local.class,
)}
{...rest}
>
{local.children}
</NavigationMenuPrimitive.Content>
</NavigationMenuPrimitive.Portal>
);
};

View file

@ -1,214 +0,0 @@
import { cn } from "../libs/cn";
import type {
NumberFieldDecrementTriggerProps,
NumberFieldDescriptionProps,
NumberFieldErrorMessageProps,
NumberFieldIncrementTriggerProps,
NumberFieldInputProps,
NumberFieldLabelProps,
NumberFieldRootProps,
} from "@kobalte/core/number-field";
import { NumberField as NumberFieldPrimitive } from "@kobalte/core/number-field";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ComponentProps, ValidComponent, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
import { textfieldLabel } from "./textfield";
export const NumberFieldHiddenInput = NumberFieldPrimitive.HiddenInput;
type numberFieldLabelProps<T extends ValidComponent = "div"> =
NumberFieldLabelProps<T> & {
class?: string;
};
export const NumberFieldLabel = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, numberFieldLabelProps<T>>,
) => {
const [local, rest] = splitProps(props as numberFieldLabelProps, ["class"]);
return (
<NumberFieldPrimitive.Label
class={cn(textfieldLabel({ label: true }), local.class)}
{...rest}
/>
);
};
type numberFieldDescriptionProps<T extends ValidComponent = "div"> =
NumberFieldDescriptionProps<T> & {
class?: string;
};
export const NumberFieldDescription = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, numberFieldDescriptionProps<T>>,
) => {
const [local, rest] = splitProps(props as numberFieldDescriptionProps, [
"class",
]);
return (
<NumberFieldPrimitive.Description
class={cn(
textfieldLabel({ description: true, label: false }),
local.class,
)}
{...rest}
/>
);
};
type numberFieldErrorMessageProps<T extends ValidComponent = "div"> =
NumberFieldErrorMessageProps<T> & {
class?: string;
};
export const NumberFieldErrorMessage = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, numberFieldErrorMessageProps<T>>,
) => {
const [local, rest] = splitProps(props as numberFieldErrorMessageProps, [
"class",
]);
return (
<NumberFieldPrimitive.ErrorMessage
class={cn(textfieldLabel({ error: true }), local.class)}
{...rest}
/>
);
};
type numberFieldProps<T extends ValidComponent = "div"> =
NumberFieldRootProps<T> & {
class?: string;
};
export const NumberField = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, numberFieldProps<T>>,
) => {
const [local, rest] = splitProps(props as numberFieldProps, ["class"]);
return (
<NumberFieldPrimitive class={cn("grid gap-1.5", local.class)} {...rest} />
);
};
export const NumberFieldGroup = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div
class={cn(
"relative rounded-md transition-shadow focus-within:outline-none focus-within:ring-[1.5px] focus-within:ring-ring",
local.class,
)}
{...rest}
/>
);
};
type numberFieldInputProps<T extends ValidComponent = "input"> =
NumberFieldInputProps<T> & {
class?: string;
};
export const NumberFieldInput = <T extends ValidComponent = "input">(
props: PolymorphicProps<T, VoidProps<numberFieldInputProps<T>>>,
) => {
const [local, rest] = splitProps(props as numberFieldInputProps, ["class"]);
return (
<NumberFieldPrimitive.Input
class={cn(
"flex h-9 w-full rounded-md border border-input bg-transparent px-10 py-1 text-center text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
local.class,
)}
{...rest}
/>
);
};
type numberFieldDecrementTriggerProps<T extends ValidComponent = "button"> =
VoidProps<
NumberFieldDecrementTriggerProps<T> & {
class?: string;
}
>;
export const NumberFieldDecrementTrigger = <
T extends ValidComponent = "button",
>(
props: PolymorphicProps<T, VoidProps<numberFieldDecrementTriggerProps<T>>>,
) => {
const [local, rest] = splitProps(props as numberFieldDecrementTriggerProps, [
"class",
]);
return (
<NumberFieldPrimitive.DecrementTrigger
class={cn(
"absolute left-0 top-1/2 -translate-y-1/2 p-3 disabled:cursor-not-allowed disabled:opacity-20",
local.class,
)}
{...rest}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="size-4"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 12h14"
/>
<title>Decreasing number</title>
</svg>
</NumberFieldPrimitive.DecrementTrigger>
);
};
type numberFieldIncrementTriggerProps<T extends ValidComponent = "button"> =
VoidProps<
NumberFieldIncrementTriggerProps<T> & {
class?: string;
}
>;
export const NumberFieldIncrementTrigger = <
T extends ValidComponent = "button",
>(
props: PolymorphicProps<T, numberFieldIncrementTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as numberFieldIncrementTriggerProps, [
"class",
]);
return (
<NumberFieldPrimitive.IncrementTrigger
class={cn(
"absolute right-0 top-1/2 -translate-y-1/2 p-3 disabled:cursor-not-allowed disabled:opacity-20",
local.class,
)}
{...rest}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="size-4"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 5v14m-7-7h14"
/>
<title>Increase number</title>
</svg>
</NumberFieldPrimitive.IncrementTrigger>
);
};

View file

@ -1,83 +0,0 @@
import { cn } from "../libs/cn";
import type { DynamicProps, RootProps } from "@corvu/otp-field";
import OTPFieldPrimitive from "@corvu/otp-field";
import type { ComponentProps, ValidComponent } from "solid-js";
import { Show, splitProps } from "solid-js";
export const OTPFieldInput = OTPFieldPrimitive.Input;
type OTPFieldProps<T extends ValidComponent = "div"> = RootProps<T> & {
class?: string;
};
export const OTPField = <T extends ValidComponent = "div">(
props: DynamicProps<T, OTPFieldProps<T>>,
) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<OTPFieldPrimitive
class={cn(
"flex items-center gap-2 has-[:disabled]:opacity-50",
local.class,
)}
{...rest}
/>
);
};
export const OTPFieldGroup = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return <div class={cn("flex items-center", local.class)} {...rest} />;
};
export const OTPFieldSeparator = (props: ComponentProps<"div">) => {
return (
// biome-ignore lint/a11y/useAriaPropsForRole: []
<div role="separator" {...props}>
<svg
xmlns="http://www.w3.org/2000/svg"
class="size-4"
viewBox="0 0 15 15"
>
<title>Separator</title>
<path
fill="currentColor"
fill-rule="evenodd"
d="M5 7.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5"
clip-rule="evenodd"
/>
</svg>
</div>
);
};
export const OTPFieldSlot = (
props: ComponentProps<"div"> & { index: number },
) => {
const [local, rest] = splitProps(props, ["class", "index"]);
const context = OTPFieldPrimitive.useContext();
const char = () => context.value()[local.index];
const hasFakeCaret = () =>
context.value().length === local.index && context.isInserting();
const isActive = () => context.activeSlots().includes(local.index);
return (
<div
class={cn(
"relative flex size-9 items-center justify-center border-y border-r border-input text-sm shadow-sm transition-shadow first:rounded-l-md first:border-l last:rounded-r-md",
isActive() && "z-10 ring-[1.5px] ring-ring",
local.class,
)}
{...rest}
>
{char()}
<Show when={hasFakeCaret()}>
<div class="pointer-events-none absolute inset-0 flex items-center justify-center">
<div class="h-4 w-px animate-caret-blink bg-foreground" />
</div>
</Show>
</div>
);
};

View file

@ -1,194 +0,0 @@
import { cn } from "../libs/cn";
import type {
PaginationEllipsisProps,
PaginationItemProps,
PaginationPreviousProps,
PaginationRootProps,
} from "@kobalte/core/pagination";
import { Pagination as PaginationPrimitive } from "@kobalte/core/pagination";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { VariantProps } from "class-variance-authority";
import type { ValidComponent, VoidProps } from "solid-js";
import { mergeProps, splitProps } from "solid-js";
import { buttonVariants } from "./button";
export const PaginationItems = PaginationPrimitive.Items;
type paginationProps<T extends ValidComponent = "nav"> =
PaginationRootProps<T> & {
class?: string;
};
export const Pagination = <T extends ValidComponent = "nav">(
props: PolymorphicProps<T, paginationProps<T>>,
) => {
const [local, rest] = splitProps(props as paginationProps, ["class"]);
return (
<PaginationPrimitive
class={cn(
"mx-auto flex w-full justify-center [&>ul]:flex [&>ul]:flex-row [&>ul]:items-center [&>ul]:gap-1",
local.class,
)}
{...rest}
/>
);
};
type paginationItemProps<T extends ValidComponent = "button"> =
PaginationItemProps<T> &
Pick<VariantProps<typeof buttonVariants>, "size"> & {
class?: string;
};
export const PaginationItem = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, paginationItemProps<T>>,
) => {
// @ts-expect-error - required `page`
const merge = mergeProps<paginationItemProps[]>({ size: "icon" }, props);
const [local, rest] = splitProps(merge as paginationItemProps, [
"class",
"size",
]);
return (
<PaginationPrimitive.Item
class={cn(
buttonVariants({
variant: "ghost",
size: local.size,
}),
"aria-[current=page]:border aria-[current=page]:border-input aria-[current=page]:bg-background aria-[current=page]:shadow-sm aria-[current=page]:hover:bg-accent aria-[current=page]:hover:text-accent-foreground",
local.class,
)}
{...rest}
/>
);
};
type paginationEllipsisProps<T extends ValidComponent = "div"> = VoidProps<
PaginationEllipsisProps<T> & {
class?: string;
}
>;
export const PaginationEllipsis = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, paginationEllipsisProps<T>>,
) => {
const [local, rest] = splitProps(props as paginationEllipsisProps, ["class"]);
return (
<PaginationPrimitive.Ellipsis
class={cn("flex h-9 w-9 items-center justify-center", local.class)}
{...rest}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0m7 0a1 1 0 1 0 2 0a1 1 0 1 0-2 0m7 0a1 1 0 1 0 2 0a1 1 0 1 0-2 0"
/>
<title>More pages</title>
</svg>
</PaginationPrimitive.Ellipsis>
);
};
type paginationPreviousProps<T extends ValidComponent = "button"> =
PaginationPreviousProps<T> &
Pick<VariantProps<typeof buttonVariants>, "size"> & {
class?: string;
};
export const PaginationPrevious = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, paginationPreviousProps<T>>,
) => {
const merge = mergeProps<paginationPreviousProps<T>[]>(
{ size: "icon" },
props,
);
const [local, rest] = splitProps(merge as paginationPreviousProps, [
"class",
"size",
]);
return (
<PaginationPrimitive.Previous
class={cn(
buttonVariants({
variant: "ghost",
size: local.size,
}),
"aria-[current=page]:border aria-[current=page]:border-input aria-[current=page]:bg-background aria-[current=page]:shadow-sm aria-[current=page]:hover:bg-accent aria-[current=page]:hover:text-accent-foreground",
local.class,
)}
{...rest}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m15 6l-6 6l6 6"
/>
<title>Previous page</title>
</svg>
</PaginationPrimitive.Previous>
);
};
type paginationNextProps<T extends ValidComponent = "button"> =
paginationPreviousProps<T>;
export const PaginationNext = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, paginationNextProps<T>>,
) => {
const merge = mergeProps<paginationNextProps<T>[]>({ size: "icon" }, props);
const [local, rest] = splitProps(merge as paginationNextProps, [
"class",
"size",
]);
return (
<PaginationPrimitive.Next
class={cn(
buttonVariants({
variant: "ghost",
size: local.size,
}),
"aria-[current=page]:border aria-[current=page]:border-input aria-[current=page]:bg-background aria-[current=page]:shadow-sm aria-[current=page]:hover:bg-accent aria-[current=page]:hover:text-accent-foreground",
local.class,
)}
{...rest}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m9 6l6 6l-6 6"
/>
<title>Next page</title>
</svg>
</PaginationPrimitive.Next>
);
};

View file

@ -1,71 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type {
PopoverContentProps,
PopoverRootProps,
} from "@kobalte/core/popover";
import { Popover as PopoverPrimitive } from "@kobalte/core/popover";
import type { ParentProps, ValidComponent } from "solid-js";
import { mergeProps, splitProps } from "solid-js";
export const PopoverTrigger = PopoverPrimitive.Trigger;
export const PopoverTitle = PopoverPrimitive.Title;
export const PopoverDescription = PopoverPrimitive.Description;
export const Popover = (props: PopoverRootProps) => {
const merge = mergeProps<PopoverRootProps[]>(
{
gutter: 4,
flip: false,
},
props,
);
return <PopoverPrimitive {...merge} />;
};
type popoverContentProps<T extends ValidComponent = "div"> = ParentProps<
PopoverContentProps<T> & {
class?: string;
}
>;
export const PopoverContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, popoverContentProps<T>>,
) => {
const [local, rest] = splitProps(props as popoverContentProps, [
"class",
"children",
]);
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
class={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class,
)}
{...rest}
>
{local.children}
<PopoverPrimitive.CloseButton class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-[opacity,box-shadow] hover:opacity-100 focus:outline-none focus:ring-[1.5px] focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M18 6L6 18M6 6l12 12"
/>
<title>Close</title>
</svg>
</PopoverPrimitive.CloseButton>
</PopoverPrimitive.Content>
</PopoverPrimitive.Portal>
);
};

View file

@ -1,36 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ProgressRootProps } from "@kobalte/core/progress";
import { Progress as ProgressPrimitive } from "@kobalte/core/progress";
import type { ParentProps, ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
export const ProgressLabel = ProgressPrimitive.Label;
export const ProgressValueLabel = ProgressPrimitive.ValueLabel;
type progressProps<T extends ValidComponent = "div"> = ParentProps<
ProgressRootProps<T> & {
class?: string;
}
>;
export const Progress = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, progressProps<T>>,
) => {
const [local, rest] = splitProps(props as progressProps, [
"class",
"children",
]);
return (
<ProgressPrimitive
class={cn("flex w-full flex-col gap-2", local.class)}
{...rest}
>
{local.children}
<ProgressPrimitive.Track class="h-2 overflow-hidden rounded-full bg-primary/20">
<ProgressPrimitive.Fill class="h-full w-[--kb-progress-fill-width] bg-primary transition-all duration-500 ease-linear data-[progress=complete]:bg-primary" />
</ProgressPrimitive.Track>
</ProgressPrimitive>
);
};

View file

@ -1,39 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { RadioGroupItemControlProps } from "@kobalte/core/radio-group";
import { RadioGroup as RadioGroupPrimitive } from "@kobalte/core/radio-group";
import type { ValidComponent, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
export const RadioGroupDescription = RadioGroupPrimitive.Description;
export const RadioGroupErrorMessage = RadioGroupPrimitive.ErrorMessage;
export const RadioGroupItemDescription = RadioGroupPrimitive.ItemDescription;
export const RadioGroupItemInput = RadioGroupPrimitive.ItemInput;
export const RadioGroupItemLabel = RadioGroupPrimitive.ItemLabel;
export const RadioGroupLabel = RadioGroupPrimitive.Label;
export const RadioGroup = RadioGroupPrimitive;
export const RadioGroupItem = RadioGroupPrimitive.Item;
type radioGroupItemControlProps<T extends ValidComponent = "div"> = VoidProps<
RadioGroupItemControlProps<T> & { class?: string }
>;
export const RadioGroupItemControl = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, radioGroupItemControlProps<T>>,
) => {
const [local, rest] = splitProps(props as radioGroupItemControlProps, [
"class",
]);
return (
<RadioGroupPrimitive.ItemControl
class={cn(
"flex aspect-square h-4 w-4 items-center justify-center rounded-full border border-primary text-primary shadow transition-shadow focus:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[checked]:bg-foreground",
local.class,
)}
{...rest}
>
<RadioGroupPrimitive.ItemIndicator class="h-2 w-2 rounded-full data-[checked]:bg-background" />
</RadioGroupPrimitive.ItemControl>
);
};

View file

@ -1,63 +0,0 @@
import { cn } from "../libs/cn";
import type { DynamicProps, HandleProps, RootProps } from "@corvu/resizable";
import ResizablePrimitive from "@corvu/resizable";
import type { ValidComponent, VoidProps } from "solid-js";
import { Show, splitProps } from "solid-js";
export const ResizablePanel = ResizablePrimitive.Panel;
type resizableProps<T extends ValidComponent = "div"> = RootProps<T> & {
class?: string;
};
export const Resizable = <T extends ValidComponent = "div">(
props: DynamicProps<T, resizableProps<T>>,
) => {
const [local, rest] = splitProps(props as resizableProps, ["class"]);
return <ResizablePrimitive class={cn("size-full", local.class)} {...rest} />;
};
type resizableHandleProps<T extends ValidComponent = "button"> = VoidProps<
HandleProps<T> & {
class?: string;
withHandle?: boolean;
}
>;
export const ResizableHandle = <T extends ValidComponent = "button">(
props: DynamicProps<T, resizableHandleProps<T>>,
) => {
const [local, rest] = splitProps(props as resizableHandleProps, [
"class",
"withHandle",
]);
return (
<ResizablePrimitive.Handle
class={cn(
"flex w-px items-center justify-center bg-border transition-shadow focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring focus-visible:ring-offset-1 data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full",
local.class,
)}
{...rest}
>
<Show when={local.withHandle}>
<div class="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-2.5 w-2.5"
viewBox="0 0 15 15"
>
<path
fill="currentColor"
fill-rule="evenodd"
d="M5.5 4.625a1.125 1.125 0 1 0 0-2.25a1.125 1.125 0 0 0 0 2.25m4 0a1.125 1.125 0 1 0 0-2.25a1.125 1.125 0 0 0 0 2.25M10.625 7.5a1.125 1.125 0 1 1-2.25 0a1.125 1.125 0 0 1 2.25 0M5.5 8.625a1.125 1.125 0 1 0 0-2.25a1.125 1.125 0 0 0 0 2.25m5.125 2.875a1.125 1.125 0 1 1-2.25 0a1.125 1.125 0 0 1 2.25 0M5.5 12.625a1.125 1.125 0 1 0 0-2.25a1.125 1.125 0 0 0 0 2.25"
clip-rule="evenodd"
/>
<title>Resizable handle</title>
</svg>
</div>
</Show>
</ResizablePrimitive.Handle>
);
};

View file

@ -1,127 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type {
SelectContentProps,
SelectItemProps,
SelectTriggerProps,
} from "@kobalte/core/select";
import { Select as SelectPrimitive } from "@kobalte/core/select";
import type { ParentProps, ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
export const Select = SelectPrimitive;
export const SelectValue = SelectPrimitive.Value;
export const SelectDescription = SelectPrimitive.Description;
export const SelectErrorMessage = SelectPrimitive.ErrorMessage;
export const SelectItemDescription = SelectPrimitive.ItemDescription;
export const SelectHiddenSelect = SelectPrimitive.HiddenSelect;
export const SelectSection = SelectPrimitive.Section;
type selectTriggerProps<T extends ValidComponent = "button"> = ParentProps<
SelectTriggerProps<T> & { class?: string }
>;
export const SelectTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, selectTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as selectTriggerProps, [
"class",
"children",
]);
return (
<SelectPrimitive.Trigger
class={cn(
"flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background transition-shadow placeholder:text-muted-foreground focus:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
local.class,
)}
{...rest}
>
{local.children}
<SelectPrimitive.Icon
as="svg"
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
class="flex size-4 items-center justify-center opacity-50"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m8 9l4-4l4 4m0 6l-4 4l-4-4"
/>
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
);
};
type selectContentProps<T extends ValidComponent = "div"> =
SelectContentProps<T> & {
class?: string;
};
export const SelectContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, selectContentProps<T>>,
) => {
const [local, rest] = splitProps(props as selectContentProps, ["class"]);
return (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
class={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class,
)}
{...rest}
>
<SelectPrimitive.Listbox class="p-1 focus-visible:outline-none" />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
);
};
type selectItemProps<T extends ValidComponent = "li"> = ParentProps<
SelectItemProps<T> & { class?: string }
>;
export const SelectItem = <T extends ValidComponent = "li">(
props: PolymorphicProps<T, selectItemProps<T>>,
) => {
const [local, rest] = splitProps(props as selectItemProps, [
"class",
"children",
]);
return (
<SelectPrimitive.Item
class={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class,
)}
{...rest}
>
<SelectPrimitive.ItemIndicator class="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m5 12l5 5L20 7"
/>
<title>Checked</title>
</svg>
</SelectPrimitive.ItemIndicator>
<SelectPrimitive.ItemLabel>{local.children}</SelectPrimitive.ItemLabel>
</SelectPrimitive.Item>
);
};

View file

@ -1,26 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { SeparatorRootProps } from "@kobalte/core/separator";
import { Separator as SeparatorPrimitive } from "@kobalte/core/separator";
import type { ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
type separatorProps<T extends ValidComponent = "hr"> = SeparatorRootProps<T> & {
class?: string;
};
export const Separator = <T extends ValidComponent = "hr">(
props: PolymorphicProps<T, separatorProps<T>>,
) => {
const [local, rest] = splitProps(props as separatorProps, ["class"]);
return (
<SeparatorPrimitive
class={cn(
"shrink-0 bg-border data-[orientation=horizontal]:h-[1px] data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-[1px]",
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,148 +0,0 @@
import { cn } from "../libs/cn";
import type {
DialogContentProps,
DialogDescriptionProps,
DialogTitleProps,
} from "@kobalte/core/dialog";
import { Dialog as DialogPrimitive } from "@kobalte/core/dialog";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import type { ComponentProps, ParentProps, ValidComponent } from "solid-js";
import { mergeProps, splitProps } from "solid-js";
export const Sheet = DialogPrimitive;
export const SheetTrigger = DialogPrimitive.Trigger;
export const sheetVariants = cva(
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[expanded]:animate-in data-[closed]:animate-out data-[expanded]:duration-200 data-[closed]:duration-200",
{
variants: {
side: {
top: "inset-x-0 top-0 border-b data-[closed]:slide-out-to-top data-[expanded]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-t data-[closed]:slide-out-to-bottom data-[expanded]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[closed]:slide-out-to-left data-[expanded]:slide-in-from-left sm:max-w-sm",
right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[closed]:slide-out-to-right data-[expanded]:slide-in-from-right sm:max-w-sm",
},
},
defaultVariants: {
side: "right",
},
},
);
type sheetContentProps<T extends ValidComponent = "div"> = ParentProps<
DialogContentProps<T> &
VariantProps<typeof sheetVariants> & {
class?: string;
}
>;
export const SheetContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, sheetContentProps<T>>,
) => {
const merge = mergeProps<sheetContentProps<T>[]>({ side: "right" }, props);
const [local, rest] = splitProps(merge as sheetContentProps, [
"class",
"children",
"side",
]);
return (
<DialogPrimitive.Portal>
<DialogPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 bg-background/80 data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0",
)}
/>
<DialogPrimitive.Content
class={sheetVariants({ side: local.side, class: local.class })}
{...rest}
>
{local.children}
<DialogPrimitive.CloseButton class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-[opacity,box-shadow] hover:opacity-100 focus:outline-none focus:ring-[1.5px] focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M18 6L6 18M6 6l12 12"
/>
<title>Close</title>
</svg>
</DialogPrimitive.CloseButton>
</DialogPrimitive.Content>
</DialogPrimitive.Portal>
);
};
type sheetTitleProps<T extends ValidComponent = "h2"> = DialogTitleProps<T> & {
class?: string;
};
export const SheetTitle = <T extends ValidComponent = "h2">(
props: PolymorphicProps<T, sheetTitleProps<T>>,
) => {
const [local, rest] = splitProps(props as sheetTitleProps, ["class"]);
return (
<DialogPrimitive.Title
class={cn("text-lg font-semibold text-foreground", local.class)}
{...rest}
/>
);
};
type sheetDescriptionProps<T extends ValidComponent = "p"> =
DialogDescriptionProps<T> & {
class?: string;
};
export const SheetDescription = <T extends ValidComponent = "p">(
props: PolymorphicProps<T, sheetDescriptionProps<T>>,
) => {
const [local, rest] = splitProps(props as sheetDescriptionProps, ["class"]);
return (
<DialogPrimitive.Description
class={cn("text-sm text-muted-foreground", local.class)}
{...rest}
/>
);
};
export const SheetHeader = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div
class={cn(
"flex flex-col space-y-2 text-center sm:text-left",
local.class,
)}
{...rest}
/>
);
};
export const SheetFooter = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div
class={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,13 +0,0 @@
import { cn } from "../libs/cn";
import { type ComponentProps, splitProps } from "solid-js";
export const Skeleton = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div
class={cn("animate-pulse rounded-md bg-primary/10", local.class)}
{...rest}
/>
);
};

View file

@ -1,21 +1,5 @@
import { Toaster as Sonner } from "solid-sonner";
import { Toaster as Sonner } from "sonner";
export const Toaster = (props: Parameters<typeof Sonner>[0]) => {
return (
<Sonner
class="toaster group"
toastOptions={{
classes: {
toast:
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
description: "group-[.toast]:text-muted-foreground",
actionButton:
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
cancelButton:
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
},
}}
{...props}
/>
);
return <Sonner className="toaster group" {...props} />;
};

View file

@ -1,62 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type {
SwitchControlProps,
SwitchThumbProps,
} from "@kobalte/core/switch";
import { Switch as SwitchPrimitive } from "@kobalte/core/switch";
import type { ParentProps, ValidComponent, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
export const SwitchLabel = SwitchPrimitive.Label;
export const Switch = SwitchPrimitive;
export const SwitchErrorMessage = SwitchPrimitive.ErrorMessage;
export const SwitchDescription = SwitchPrimitive.Description;
type switchControlProps<T extends ValidComponent = "input"> = ParentProps<
SwitchControlProps<T> & { class?: string }
>;
export const SwitchControl = <T extends ValidComponent = "input">(
props: PolymorphicProps<T, switchControlProps<T>>,
) => {
const [local, rest] = splitProps(props as switchControlProps, [
"class",
"children",
]);
return (
<>
<SwitchPrimitive.Input class="[&:focus-visible+div]:outline-none [&:focus-visible+div]:ring-[1.5px] [&:focus-visible+div]:ring-ring [&:focus-visible+div]:ring-offset-2 [&:focus-visible+div]:ring-offset-background" />
<SwitchPrimitive.Control
class={cn(
"inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent bg-input shadow-sm transition-[color,background-color,box-shadow] data-[disabled]:cursor-not-allowed data-[checked]:bg-primary data-[disabled]:opacity-50",
local.class,
)}
{...rest}
>
{local.children}
</SwitchPrimitive.Control>
</>
);
};
type switchThumbProps<T extends ValidComponent = "div"> = VoidProps<
SwitchThumbProps<T> & { class?: string }
>;
export const SwitchThumb = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, switchThumbProps<T>>,
) => {
const [local, rest] = splitProps(props as switchThumbProps, ["class"]);
return (
<SwitchPrimitive.Thumb
class={cn(
"pointer-events-none block h-4 w-4 translate-x-0 rounded-full bg-background shadow-lg ring-0 transition-transform data-[checked]:translate-x-4",
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,93 +0,0 @@
import { cn } from "../libs/cn";
import { type ComponentProps, splitProps } from "solid-js";
export const Table = (props: ComponentProps<"table">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div class="w-full overflow-auto">
<table
class={cn("w-full caption-bottom text-sm", local.class)}
{...rest}
/>
</div>
);
};
export const TableHeader = (props: ComponentProps<"thead">) => {
const [local, rest] = splitProps(props, ["class"]);
return <thead class={cn("[&_tr]:border-b", local.class)} {...rest} />;
};
export const TableBody = (props: ComponentProps<"tbody">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<tbody class={cn("[&_tr:last-child]:border-0", local.class)} {...rest} />
);
};
export const TableFooter = (props: ComponentProps<"tfoot">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<tbody
class={cn("bg-primary font-medium text-primary-foreground", local.class)}
{...rest}
/>
);
};
export const TableRow = (props: ComponentProps<"tr">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<tr
class={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
local.class,
)}
{...rest}
/>
);
};
export const TableHead = (props: ComponentProps<"th">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<th
class={cn(
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
local.class,
)}
{...rest}
/>
);
};
export const TableCell = (props: ComponentProps<"td">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<td
class={cn(
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
local.class,
)}
{...rest}
/>
);
};
export const TableCaption = (props: ComponentProps<"caption">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<caption
class={cn("mt-4 text-sm text-muted-foreground", local.class)}
{...rest}
/>
);
};

View file

@ -1,133 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type {
TabsContentProps,
TabsIndicatorProps,
TabsListProps,
TabsRootProps,
TabsTriggerProps,
} from "@kobalte/core/tabs";
import { Tabs as TabsPrimitive } from "@kobalte/core/tabs";
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import type { ValidComponent, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
type tabsProps<T extends ValidComponent = "div"> = TabsRootProps<T> & {
class?: string;
};
export const Tabs = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, tabsProps<T>>,
) => {
const [local, rest] = splitProps(props as tabsProps, ["class"]);
return (
<TabsPrimitive
class={cn("w-full data-[orientation=vertical]:flex", local.class)}
{...rest}
/>
);
};
type tabsListProps<T extends ValidComponent = "div"> = TabsListProps<T> & {
class?: string;
};
export const TabsList = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, tabsListProps<T>>,
) => {
const [local, rest] = splitProps(props as tabsListProps, ["class"]);
return (
<TabsPrimitive.List
class={cn(
"relative flex w-full rounded-lg bg-muted p-1 text-muted-foreground data-[orientation=vertical]:flex-col data-[orientation=horizontal]:items-center data-[orientation=vertical]:items-stretch",
local.class,
)}
{...rest}
/>
);
};
type tabsContentProps<T extends ValidComponent = "div"> =
TabsContentProps<T> & {
class?: string;
};
export const TabsContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, tabsContentProps<T>>,
) => {
const [local, rest] = splitProps(props as tabsContentProps, ["class"]);
return (
<TabsPrimitive.Content
class={cn(
"transition-shadow duration-200 focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background data-[orientation=horizontal]:mt-2 data-[orientation=vertical]:ml-2",
local.class,
)}
{...rest}
/>
);
};
type tabsTriggerProps<T extends ValidComponent = "button"> =
TabsTriggerProps<T> & {
class?: string;
};
export const TabsTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, tabsTriggerProps<T>>,
) => {
const [local, rest] = splitProps(props as tabsTriggerProps, ["class"]);
return (
<TabsPrimitive.Trigger
class={cn(
"peer relative z-10 inline-flex h-7 w-full items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium outline-none transition-colors disabled:pointer-events-none disabled:opacity-50 data-[selected]:text-foreground",
local.class,
)}
{...rest}
/>
);
};
const tabsIndicatorVariants = cva(
"absolute transition-all duration-200 outline-none",
{
variants: {
variant: {
block:
"data-[orientation=horizontal]:bottom-1 data-[orientation=horizontal]:left-0 data-[orientation=vertical]:right-1 data-[orientation=vertical]:top-0 data-[orientation=horizontal]:h-[calc(100%-0.5rem)] data-[orientation=vertical]:w-[calc(100%-0.5rem)] bg-background shadow rounded-md peer-focus-visible:ring-[1.5px] peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-background peer-focus-visible:outline-none",
underline:
"data-[orientation=horizontal]:-bottom-[1px] data-[orientation=horizontal]:left-0 data-[orientation=vertical]:-right-[1px] data-[orientation=vertical]:top-0 data-[orientation=horizontal]:h-[2px] data-[orientation=vertical]:w-[2px] bg-primary",
},
},
defaultVariants: {
variant: "block",
},
},
);
type tabsIndicatorProps<T extends ValidComponent = "div"> = VoidProps<
TabsIndicatorProps<T> &
VariantProps<typeof tabsIndicatorVariants> & {
class?: string;
}
>;
export const TabsIndicator = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, tabsIndicatorProps<T>>,
) => {
const [local, rest] = splitProps(props as tabsIndicatorProps, [
"class",
"variant",
]);
return (
<TabsPrimitive.Indicator
class={cn(tabsIndicatorVariants({ variant: local.variant }), local.class)}
{...rest}
/>
);
};

View file

@ -1,28 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { TextFieldTextAreaProps } from "@kobalte/core/text-field";
import { TextArea as TextFieldPrimitive } from "@kobalte/core/text-field";
import type { ValidComponent, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
type textAreaProps<T extends ValidComponent = "textarea"> = VoidProps<
TextFieldTextAreaProps<T> & {
class?: string;
}
>;
export const TextArea = <T extends ValidComponent = "textarea">(
props: PolymorphicProps<T, textAreaProps<T>>,
) => {
const [local, rest] = splitProps(props as textAreaProps, ["class"]);
return (
<TextFieldPrimitive
class={cn(
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-shadow placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,129 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type {
TextFieldDescriptionProps,
TextFieldErrorMessageProps,
TextFieldInputProps,
TextFieldLabelProps,
TextFieldRootProps,
} from "@kobalte/core/text-field";
import { TextField as TextFieldPrimitive } from "@kobalte/core/text-field";
import { cva } from "class-variance-authority";
import type { ValidComponent, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
type textFieldProps<T extends ValidComponent = "div"> =
TextFieldRootProps<T> & {
class?: string;
};
export const TextFieldRoot = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, textFieldProps<T>>,
) => {
const [local, rest] = splitProps(props as textFieldProps, ["class"]);
return <TextFieldPrimitive class={cn("space-y-1", local.class)} {...rest} />;
};
export const textfieldLabel = cva(
"text-sm data-[disabled]:cursor-not-allowed data-[disabled]:opacity-70 font-medium",
{
variants: {
label: {
true: "data-[invalid]:text-destructive",
},
error: {
true: "text-destructive text-xs",
},
description: {
true: "font-normal text-muted-foreground",
},
},
defaultVariants: {
label: true,
},
},
);
type textFieldLabelProps<T extends ValidComponent = "label"> =
TextFieldLabelProps<T> & {
class?: string;
};
export const TextFieldLabel = <T extends ValidComponent = "label">(
props: PolymorphicProps<T, textFieldLabelProps<T>>,
) => {
const [local, rest] = splitProps(props as textFieldLabelProps, ["class"]);
return (
<TextFieldPrimitive.Label
class={cn(textfieldLabel(), local.class)}
{...rest}
/>
);
};
type textFieldErrorMessageProps<T extends ValidComponent = "div"> =
TextFieldErrorMessageProps<T> & {
class?: string;
};
export const TextFieldErrorMessage = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, textFieldErrorMessageProps<T>>,
) => {
const [local, rest] = splitProps(props as textFieldErrorMessageProps, [
"class",
]);
return (
<TextFieldPrimitive.ErrorMessage
class={cn(textfieldLabel({ error: true }), local.class)}
{...rest}
/>
);
};
type textFieldDescriptionProps<T extends ValidComponent = "div"> =
TextFieldDescriptionProps<T> & {
class?: string;
};
export const TextFieldDescription = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, textFieldDescriptionProps<T>>,
) => {
const [local, rest] = splitProps(props as textFieldDescriptionProps, [
"class",
]);
return (
<TextFieldPrimitive.Description
class={cn(
textfieldLabel({ description: true, label: false }),
local.class,
)}
{...rest}
/>
);
};
type textFieldInputProps<T extends ValidComponent = "input"> = VoidProps<
TextFieldInputProps<T> & {
class?: string;
}
>;
export const TextField = <T extends ValidComponent = "input">(
props: PolymorphicProps<T, textFieldInputProps<T>>,
) => {
const [local, rest] = splitProps(props as textFieldInputProps, ["class"]);
return (
<TextFieldPrimitive.Input
class={cn(
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-shadow file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,168 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type {
ToastDescriptionProps,
ToastListProps,
ToastRegionProps,
ToastRootProps,
ToastTitleProps,
} from "@kobalte/core/toast";
import { Toast as ToastPrimitive } from "@kobalte/core/toast";
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import type {
ComponentProps,
ValidComponent,
VoidComponent,
VoidProps,
} from "solid-js";
import { mergeProps, splitProps } from "solid-js";
import { Portal } from "solid-js/web";
export const toastVariants = cva(
"group pointer-events-auto relative flex flex-col gap-3 w-full items-center justify-between overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-y-0 data-[swipe=end]:translate-y-[var(--kb-toast-swipe-end-y)] data-[swipe=move]:translate-y-[--kb-toast-swipe-move-y] data-[swipe=move]:transition-none data-[opened]:animate-in data-[closed]:animate-out data-[swipe=end]:animate-out data-[closed]:fade-out-80 data-[closed]:slide-out-to-top-full data-[closed]:sm:slide-out-to-bottom-full data-[opened]:slide-in-from-top-full data-[opened]:sm:slide-in-from-bottom-full",
{
variants: {
variant: {
default: "border bg-background",
destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
);
type toastProps<T extends ValidComponent = "li"> = ToastRootProps<T> &
VariantProps<typeof toastVariants> & {
class?: string;
};
export const Toast = <T extends ValidComponent = "li">(
props: PolymorphicProps<T, toastProps<T>>,
) => {
const [local, rest] = splitProps(props as toastProps, ["class", "variant"]);
return (
<ToastPrimitive
class={cn(toastVariants({ variant: local.variant }), local.class)}
{...rest}
/>
);
};
type toastTitleProps<T extends ValidComponent = "div"> = ToastTitleProps<T> & {
class?: string;
};
export const ToastTitle = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, toastTitleProps<T>>,
) => {
const [local, rest] = splitProps(props as toastTitleProps, ["class"]);
return (
<ToastPrimitive.Title
class={cn("text-sm font-semibold [&+div]:text-xs", local.class)}
{...rest}
/>
);
};
type toastDescriptionProps<T extends ValidComponent = "div"> =
ToastDescriptionProps<T> & {
class?: string;
};
export const ToastDescription = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, toastDescriptionProps<T>>,
) => {
const [local, rest] = splitProps(props as toastDescriptionProps, ["class"]);
return (
<ToastPrimitive.Description
class={cn("text-sm opacity-90", local.class)}
{...rest}
/>
);
};
type toastRegionProps<T extends ValidComponent = "div"> =
ToastRegionProps<T> & {
class?: string;
};
export const ToastRegion = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, toastRegionProps<T>>,
) => {
const merge = mergeProps<toastRegionProps[]>(
{
swipeDirection: "down",
},
props,
);
return (
<Portal>
<ToastPrimitive.Region {...merge} />
</Portal>
);
};
type toastListProps<T extends ValidComponent = "ol"> = VoidProps<
ToastListProps<T> & {
class?: string;
}
>;
export const ToastList = <T extends ValidComponent = "ol">(
props: PolymorphicProps<T, toastListProps<T>>,
) => {
const [local, rest] = splitProps(props as toastListProps, ["class"]);
return (
<ToastPrimitive.List
class={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse gap-2 p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
local.class,
)}
{...rest}
/>
);
};
export const ToastContent = (props: ComponentProps<"div">) => {
const [local, rest] = splitProps(props, ["class", "children"]);
return (
<div class={cn("flex w-full flex-col", local.class)} {...rest}>
<div>{local.children}</div>
<ToastPrimitive.CloseButton class="absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M18 6L6 18M6 6l12 12"
/>
<title>Close</title>
</svg>
</ToastPrimitive.CloseButton>
</div>
);
};
export const ToastProgress: VoidComponent = () => {
return (
<ToastPrimitive.ProgressTrack class="h-1 w-full overflow-hidden rounded-xl bg-primary/20 group-[.destructive]:bg-background/20">
<ToastPrimitive.ProgressFill class="h-full w-[--kb-toast-progress-fill-width] bg-primary transition-all duration-150 ease-linear group-[.destructive]:bg-destructive-foreground" />
</ToastPrimitive.ProgressTrack>
);
};

View file

@ -1,85 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type {
ToggleGroupItemProps,
ToggleGroupRootProps,
} from "@kobalte/core/toggle-group";
import { ToggleGroup as ToggleGroupPrimitive } from "@kobalte/core/toggle-group";
import type { VariantProps } from "class-variance-authority";
import type { Accessor, ParentProps, ValidComponent } from "solid-js";
import { createContext, createMemo, splitProps, useContext } from "solid-js";
import { toggleVariants } from "./toggle";
const ToggleGroupContext =
createContext<Accessor<VariantProps<typeof toggleVariants>>>();
const useToggleGroup = () => {
const context = useContext(ToggleGroupContext);
if (!context) {
throw new Error(
"`useToggleGroup`: must be used within a `ToggleGroup` component",
);
}
return context;
};
type toggleGroupProps<T extends ValidComponent = "div"> = ParentProps<
ToggleGroupRootProps<T> &
VariantProps<typeof toggleVariants> & {
class?: string;
}
>;
export const ToggleGroup = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, toggleGroupProps<T>>,
) => {
const [local, rest] = splitProps(props as toggleGroupProps, [
"class",
"children",
"size",
"variant",
]);
const value = createMemo<VariantProps<typeof toggleVariants>>(() => ({
size: local.size,
variant: local.variant,
}));
return (
<ToggleGroupPrimitive
class={cn("flex items-center justify-center gap-1", local.class)}
{...rest}
>
<ToggleGroupContext.Provider value={value}>
{local.children}
</ToggleGroupContext.Provider>
</ToggleGroupPrimitive>
);
};
type toggleGroupItemProps<T extends ValidComponent = "button"> =
ToggleGroupItemProps<T> & {
class?: string;
};
export const ToggleGroupItem = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, toggleGroupItemProps<T>>,
) => {
const [local, rest] = splitProps(props as toggleGroupItemProps, ["class"]);
const context = useToggleGroup();
return (
<ToggleGroupPrimitive.Item
class={cn(
toggleVariants({
variant: context().variant,
size: context().size,
}),
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,56 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type { ToggleButtonRootProps } from "@kobalte/core/toggle-button";
import { ToggleButton as ToggleButtonPrimitive } from "@kobalte/core/toggle-button";
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import type { ValidComponent } from "solid-js";
import { splitProps } from "solid-js";
export const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-[box-shadow,color,background-color] hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[pressed]:bg-accent data-[pressed]:text-accent-foreground",
{
variants: {
variant: {
default: "bg-transparent",
outline:
"border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
},
size: {
default: "h-9 px-3",
sm: "h-8 px-2",
lg: "h-10 px-3",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
type toggleButtonProps<T extends ValidComponent = "button"> =
ToggleButtonRootProps<T> &
VariantProps<typeof toggleVariants> & {
class?: string;
};
export const ToggleButton = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, toggleButtonProps<T>>,
) => {
const [local, rest] = splitProps(props as toggleButtonProps, [
"class",
"variant",
"size",
]);
return (
<ToggleButtonPrimitive
class={cn(
toggleVariants({ variant: local.variant, size: local.size }),
local.class,
)}
{...rest}
/>
);
};

View file

@ -1,45 +0,0 @@
import { cn } from "../libs/cn";
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import type {
TooltipContentProps,
TooltipRootProps,
} from "@kobalte/core/tooltip";
import { Tooltip as TooltipPrimitive } from "@kobalte/core/tooltip";
import { type ValidComponent, mergeProps, splitProps } from "solid-js";
export const TooltipTrigger = TooltipPrimitive.Trigger;
export const Tooltip = (props: TooltipRootProps) => {
const merge = mergeProps<TooltipRootProps[]>(
{
gutter: 4,
flip: false,
},
props,
);
return <TooltipPrimitive {...merge} />;
};
type tooltipContentProps<T extends ValidComponent = "div"> =
TooltipContentProps<T> & {
class?: string;
};
export const TooltipContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, tooltipContentProps<T>>,
) => {
const [local, rest] = splitProps(props as tooltipContentProps, ["class"]);
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
class={cn(
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class,
)}
{...rest}
/>
</TooltipPrimitive.Portal>
);
};

View file

@ -3,8 +3,7 @@
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"noEmit": true,
@ -13,5 +12,18 @@
"@/*": ["./src/*"]
}
},
"include": ["src"]
"include": [
"src/link.tsx",
"src/libs/cn.ts",
"src/screens/error.tsx",
"src/screens/loading.tsx",
"src/ui/avatar.tsx",
"src/ui/button.tsx",
"src/ui/card.tsx",
"src/ui/checkbox.tsx",
"src/ui/context-menu.tsx",
"src/ui/input.tsx",
"src/ui/label.tsx",
"src/ui/sonner.tsx"
]
}

View file

@ -18,23 +18,17 @@
"@tensamin/core-user": "workspace:*",
"@tensamin/shared": "workspace:*",
"@tensamin/ui": "workspace:*",
"@ark-ui/solid": "^5.34.1",
"@corvu/drawer": "^0.2.4",
"@corvu/otp-field": "^0.1.4",
"@corvu/resizable": "^0.2.5",
"@kobalte/core": "^0.13.11",
"@noble/curves": "^2.0.1",
"@solidjs/router": "^0.15.4",
"@tailwindcss/vite": "^4.2.1",
"@tanstack/solid-virtual": "^3.13.21",
"@tanstack/react-router": "^1.0.0",
"@tanstack/react-virtual": "^3.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk-solid": "^1.1.2",
"comlink": "^4.4.2",
"embla-carousel-solid": "^8.6.0",
"lucide-solid": "^0.564.0",
"solid-js": "^1.9.11",
"solid-sonner": "^0.2.8",
"lucide-react": "^0.564.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"sonner": "^1.0.0",
"tailwind-merge": "^3.5.0",
"tailwind-scrollbar-hide": "^4.0.0",
"tailwindcss": "^4.2.1",
@ -43,14 +37,14 @@
"zod": "^4.3.6"
},
"devDependencies": {
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@vitejs/plugin-react": "^5.0.4",
"@eslint/js": "^10.0.1",
"eslint": "^10.0.3",
"eslint-plugin-solid": "^0.14.5",
"globals": "^17.4.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.57.0",
"vite": "^7.3.1",
"vite-plugin-lucide-preprocess": "^1.4.8",
"vite-plugin-solid": "^2.11.10"
"vite": "^7.3.1"
}
}

View file

@ -5,13 +5,13 @@ import { Card, CardHeader } from "@tensamin/ui/card";
export default function Basic(props: { user: User }) {
return (
<Card class="animate-in fade-in duration-300 rounded-2xl">
<CardHeader class="flex flex-row gap-2.5 items-center justify-start p-2">
<Card className="animate-in fade-in duration-300 rounded-2xl">
<CardHeader className="flex flex-row gap-2.5 items-center justify-start p-2">
<Avatar
img={props.user.avatar}
fallback={reduceDisplay(props.user.display)}
/>
<div class="flex flex-col gap-1 w-full items-start justify-center text-[15px]">
<div className="flex flex-col gap-1 w-full items-start justify-center text-[15px]">
<p>{props.user.display}</p>
</div>
</CardHeader>

View file

@ -1,42 +1,45 @@
import { Button } from "@tensamin/ui/button";
import { House } from "lucide-solid";
import { useNavigate, useSearchParams } from "@solidjs/router";
import { createEffect, createSignal } from "solid-js";
import { House } from "lucide-react";
import { useNavigate, useRouterState } from "@tanstack/react-router";
import * as React from "react";
import { useUser, type User } from "@tensamin/core-user/context";
export default function Navbar() {
const navigate = useNavigate();
const { get } = useUser();
const search = useRouterState({ select: (state) => state.location.search });
const [user, setUser] = createSignal<User | null>(null);
const [user, setUser] = React.useState<User | null>(null);
createEffect(() => {
const [searchParams] = useSearchParams();
const id = Number(searchParams.id);
const currentId = React.useMemo(
() => Number((search as Record<string, unknown>).id ?? Number.NaN),
[search],
);
if (isNaN(id)) {
React.useEffect(() => {
if (Number.isNaN(currentId)) {
setUser(null);
return;
}
get(id)
get(currentId)
.then(setUser)
.catch(() => setUser(null));
});
}, [currentId, get]);
return (
<div class="w-full h-13.5 flex items-center justify-center">
<div className="w-full h-13.5 flex items-center justify-center">
<Button
onClick={() => {
navigate("/");
void navigate({ to: "/" });
}}
class="w-9 h-9 aspect-square p-0 rounded-lg"
className="w-9 h-9 aspect-square p-0 rounded-lg"
variant="outline"
>
<House size={18} />
</Button>
<p class="font-medium pl-3 text-md">{user()?.display}</p>
<div class="w-full" />
<p className="font-medium pl-3 text-md">{user?.display}</p>
<div className="w-full" />
</div>
);
}

View file

@ -4,8 +4,9 @@ import { Input } from "@tensamin/ui/input";
import { Label } from "@tensamin/ui/label";
import { useStorage } from "@tensamin/core-storage/context";
import { log, toast } from "@tensamin/shared/log";
import { useNavigate } from "@solidjs/router";
import { Upload } from "lucide-solid";
import { useNavigate } from "@tanstack/react-router";
import { Upload } from "lucide-react";
import * as React from "react";
import { z } from "zod";
const fetchedUser = z.object({
@ -27,25 +28,22 @@ const formSchema = z.object({
});
export default function Form() {
let uploadRef: HTMLInputElement | undefined = undefined;
const setUploadRef = (el: HTMLInputElement) => {
uploadRef = el;
};
const uploadRef = React.useRef<HTMLInputElement | null>(null);
const { save } = useStorage();
const navigate = useNavigate();
return (
<div class="flex gap-5">
<Card class="w-75 h-80">
<div className="flex gap-5">
<Card className="w-75 h-80">
<CardHeader>
<CardTitle>Use .tu file</CardTitle>
</CardHeader>
<CardContent class="h-full flex items-center justify-center">
<CardContent className="h-full flex items-center justify-center">
<div
onClick={() => uploadRef?.click()}
class="cursor-pointer w-60 aspect-square mb-17 bg-input/13 hover:bg-input/30 transition-all duration-300 ease-in-out border-dotted border-input/75 border-3 flex items-center justify-center rounded-lg"
onClick={() => uploadRef.current?.click()}
className="cursor-pointer w-60 aspect-square mb-17 bg-input/13 hover:bg-input/30 transition-all duration-300 ease-in-out border-dotted border-input/75 border-3 flex items-center justify-center rounded-lg"
>
<Upload class="text-input/75" size={34} />
<Upload className="text-input/75" size={34} />
</div>
<input
onChange={async (e) => {
@ -62,7 +60,7 @@ export default function Form() {
save("user_id", userId);
save("private_key", privateKey);
navigate("/");
void navigate({ to: "/" });
} else {
throw new Error("No file selected");
}
@ -72,18 +70,18 @@ export default function Form() {
}
}}
type="file"
ref={setUploadRef}
class="hidden"
ref={uploadRef}
className="hidden"
/>
</CardContent>
</Card>
<Card class="w-75 h-auto">
<Card className="w-75 h-auto">
<CardHeader>
<CardTitle>Use credentials</CardTitle>
</CardHeader>
<CardContent>
<form
class="flex flex-col gap-5 h-full"
className="flex flex-col gap-5 h-full"
onSubmit={async (e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
@ -111,7 +109,7 @@ export default function Form() {
save("user_id", user.data.user_id);
save("private_key", inputParse.data.private_key);
navigate("/");
void navigate({ to: "/" });
} else {
log(0, "Login", "red", "Invalid response from server");
toast("error", "Invalid response from server");
@ -123,13 +121,13 @@ export default function Form() {
});
}}
>
<div class="flex flex-col gap-2">
<Label for="username">Username</Label>
<Input type="text" id="username" />
<div className="flex flex-col gap-2">
<Label htmlFor="username">Username</Label>
<Input type="text" id="username" name="username" />
</div>
<div class="flex flex-col gap-2">
<Label for="private_key">Private Key</Label>
<Input type="password" id="private_key" />
<div className="flex flex-col gap-2">
<Label htmlFor="private_key">Private Key</Label>
<Input type="password" id="private_key" name="private_key" />
</div>
<Button type="submit">Login</Button>
</form>

View file

@ -1,26 +1,26 @@
import { useStorage } from "@tensamin/core-storage/context";
import Wrapper from "@tensamin/core-user/wrapper";
import { createEffect, createSignal } from "solid-js";
import * as React from "react";
import Basic from "./modals/basic";
import List from "@/features/conversation/list/body";
export default function Sidebar() {
const [userId, setUserId] = createSignal(0);
const [userId, setUserId] = React.useState(0);
const { load } = useStorage();
createEffect(() => {
React.useEffect(() => {
load("user_id").then(setUserId);
});
}, [load]);
return (
<div class="w-75 h-full flex flex-col gap-3 p-2">
{userId() !== 0 && (
<div className="w-75 h-full flex flex-col gap-3 p-2">
{userId !== 0 && (
<Wrapper
userId={userId()}
userId={userId}
component={(user) => <Basic user={user} />}
/>
)}
<div class="h-full">
<div className="h-full">
<List />
</div>
</div>

View file

@ -1,10 +1,4 @@
import {
createContext,
createEffect,
useContext,
type ParentProps,
} from "solid-js";
import { createStore } from "solid-js/store";
import * as React from "react";
import { useSocket } from "@tensamin/ttp/context";
import { toast } from "@tensamin/shared/log";
@ -18,40 +12,60 @@ interface contextValue {
communities: Community[];
}
const ConversationContext = createContext<contextValue>();
const ConversationContext = React.createContext<contextValue | undefined>(
undefined,
);
export default function ConversationProvider(props: ParentProps) {
const [conversations, setConversations] = createStore<Conversation[]>([]);
const [communities, setCommunities] = createStore<Community[]>([]);
export default function ConversationProvider(props: {
children: React.ReactNode;
}) {
const [conversations, setConversations] = React.useState<Conversation[]>([]);
const [communities, setCommunities] = React.useState<Community[]>([]);
const { send } = useSocket();
createEffect(() => {
React.useEffect(() => {
let active = true;
send("get_chats", {})
.then((data) => {
if (active) {
setConversations(data.data.user_ids);
}
})
.catch(() => {
toast("error", "Failed to load conversations");
});
send("get_communities", {})
.then((data) => {
if (active) {
setCommunities(data.data.communities);
}
})
.catch(() => {
toast("error", "Failed to load communities");
});
});
return () => {
active = false;
};
}, [send]);
const value = React.useMemo(
() => ({ conversations, communities }),
[communities, conversations],
);
return (
<ConversationContext.Provider value={{ conversations, communities }}>
<ConversationContext.Provider value={value}>
{props.children}
</ConversationContext.Provider>
);
}
export function useConversation(): contextValue {
const context = useContext(ConversationContext);
const context = React.useContext(ConversationContext);
if (!context) {
throw new Error(
"useConversation must be used within a ConversationProvider",

View file

@ -1,63 +1,62 @@
import { createSignal, For, Show } from "solid-js";
import * as React from "react";
import { useVirtualizer } from "@tanstack/react-virtual";
import { useConversation } from "../context";
import { createVirtualizer } from "@tanstack/solid-virtual";
import Switch from "./switch";
import ConversationModal from "../modal/conversation";
import CommunityModal from "../modal/community";
export default function List() {
const [category, setCategory] = createSignal<"conversations" | "communities">(
"conversations",
);
const [category, setCategory] = React.useState<
"conversations" | "communities"
>("conversations");
const { conversations, communities } = useConversation();
// eslint-disable-next-line no-unassigned-vars
let scrollRef!: HTMLDivElement;
const scrollRef = React.useRef<HTMLDivElement | null>(null);
const virtualizer = createVirtualizer({
get count() {
return category() === "conversations"
? conversations.length
: communities.length;
},
const virtualizer = useVirtualizer({
count: category === "conversations" ? conversations.length : communities.length,
estimateSize: () => 80,
getScrollElement: () => scrollRef,
getScrollElement: () => scrollRef.current,
});
return (
<div class="flex flex-col gap-3">
<Switch category={category()} setCategory={setCategory} />
<div className="flex flex-col gap-3">
<Switch category={category} setCategory={setCategory} />
<div ref={scrollRef} id="conversation-list" className="overflow-y-auto flex-1">
<div
ref={scrollRef}
id="conversation-list"
class="overflow-y-auto flex-1"
>
<div
class="relative w-full flex flex-col gap-2"
className="relative w-full flex flex-col gap-2"
style={{
height: `${virtualizer.getTotalSize()}px`,
}}
>
<For each={virtualizer.getVirtualItems()}>
{(virtualItem) => {
{virtualizer.getVirtualItems().map((virtualItem) => {
const itemKey = `${category}-${virtualItem.index}`;
return (
<Show
when={category() === "conversations"}
fallback={
<CommunityModal
community={communities[virtualItem.index]}
/>
}
<div
key={itemKey}
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
transform: `translateY(${virtualItem.start}px)`,
}}
>
{category === "conversations" ? (
conversations[virtualItem.index] ? (
<ConversationModal
userId={conversations[virtualItem.index].user_id}
/>
</Show>
) : null
) : communities[virtualItem.index] ? (
<CommunityModal community={communities[virtualItem.index]} />
) : null}
</div>
);
}}
</For>
})}
</div>
</div>
</div>

View file

@ -1,4 +1,4 @@
import { createEffect, createSignal, onMount } from "solid-js";
import * as React from "react";
type Category = "conversations" | "communities";
@ -6,24 +6,27 @@ export default function Switch(props: {
category: Category;
setCategory: (category: Category) => void;
}) {
let conversationsRef: HTMLButtonElement | undefined;
let communitiesRef: HTMLButtonElement | undefined;
const conversationsRef = React.useRef<HTMLButtonElement | null>(null);
const communitiesRef = React.useRef<HTMLButtonElement | null>(null);
const [indicator, setIndicator] = createSignal({ left: 0, width: 0 });
const [indicator, setIndicator] = React.useState({ left: 0, width: 0 });
function updateIndicator() {
const updateIndicator = React.useCallback(() => {
const active =
props.category === "conversations" ? conversationsRef : communitiesRef;
props.category === "conversations"
? conversationsRef.current
: communitiesRef.current;
if (!active) return;
setIndicator({
left: active.offsetLeft,
width: active.offsetWidth,
});
}
}, [props.category]);
onMount(updateIndicator);
createEffect(updateIndicator);
React.useLayoutEffect(() => {
updateIndicator();
}, [updateIndicator]);
function toggleCategory() {
props.setCategory(
@ -34,20 +37,20 @@ export default function Switch(props: {
return (
<div
role="tablist"
class="border relative inline-flex rounded-full bg-card p-1 select-none"
className="border relative inline-flex rounded-full bg-card p-1 select-none"
>
<div
class="absolute top-1 bottom-1 rounded-full bg-input shadow-sm transition-all duration-300 ease-in-out"
className="absolute top-1 bottom-1 rounded-full bg-input shadow-sm transition-all duration-300 ease-in-out"
style={{
left: `${indicator().left}px`,
width: `${indicator().width}px`,
left: `${indicator.left}px`,
width: `${indicator.width}px`,
}}
/>
<button
ref={(el) => (conversationsRef = el)}
ref={conversationsRef}
role="tab"
aria-selected={props.category === "conversations"}
class={`relative z-10 cursor-pointer px-2.5 py-1.5 rounded-full text-sm font-medium transition-colors duration-300 ${
className={`relative z-10 cursor-pointer px-2.5 py-1.5 rounded-full text-sm font-medium transition-colors duration-300 ${
props.category === "conversations"
? "text-foreground"
: "text-ring/50 hover:text-ring"
@ -57,10 +60,10 @@ export default function Switch(props: {
Conversations
</button>
<button
ref={(el) => (communitiesRef = el)}
ref={communitiesRef}
role="tab"
aria-selected={props.category === "communities"}
class={`relative z-10 cursor-pointer px-2.5 py-1.5 rounded-full text-sm font-medium transition-colors duration-300 ${
className={`relative z-10 cursor-pointer px-2.5 py-1.5 rounded-full text-sm font-medium transition-colors duration-300 ${
props.category === "communities"
? "text-foreground"
: "text-ring/50 hover:text-ring"

View file

@ -7,7 +7,7 @@ import {
ContextMenuItem,
ContextMenuTrigger,
} from "@tensamin/ui/context-menu";
import { useNavigate } from "@solidjs/router";
import { useNavigate } from "@tanstack/react-router";
export default function ConversationModal(props: { userId: number }) {
const navigate = useNavigate();
@ -16,9 +16,9 @@ export default function ConversationModal(props: { userId: number }) {
<ContextMenu>
<ContextMenuTrigger>
<div
class="select-none cursor-pointer"
className="select-none cursor-pointer"
onClick={() => {
navigate("/chat?id=" + props.userId);
void navigate({ to: "/chat", search: { id: props.userId } });
}}
>
<Wrapper

View file

@ -1,3 +1,4 @@
import * as React from "react";
import { useStorage } from "@tensamin/core-storage/context";
import { Button } from "@tensamin/ui/button";
import {
@ -5,7 +6,6 @@ import {
CheckboxLabel,
CheckboxControl,
} from "@tensamin/ui/checkbox";
import { createEffect, createSignal, type JSX, Show } from "solid-js";
import { z } from "zod";
import ErrorScreen from "@tensamin/ui/screens/error";
@ -14,20 +14,34 @@ import { legalDocsSchema } from "@tensamin/shared/features/legal/schema";
import { log } from "@tensamin/shared/log";
import Link from "@tensamin/ui/link";
export default function Screen(props: { children: JSX.Element }) {
export default function Screen(props: { children: React.ReactNode }) {
const { load, save } = useStorage();
const [error, setError] = createSignal("");
const [errorDescription, setErrorDescription] = createSignal("");
const [error, setError] = React.useState("");
const [errorDescription, setErrorDescription] = React.useState("");
const [remoteDocs, setRemoteDocs] =
createSignal<z.infer<typeof legalDocsSchema>>();
const [remoteDocs, setRemoteDocs] = React.useState<
z.infer<typeof legalDocsSchema> | undefined
>(undefined);
const [loading, setLoading] = createSignal(true);
const [loading, setLoading] = React.useState(true);
const [PPandToSDone, setPPandToSDone] = React.useState(false);
const [acceptedPP, acceptPP] = React.useState(false);
const [acceptedTOS, acceptTOS] = React.useState(false);
const [doneWithAnalytics, setDoneWithAnalytics] = React.useState(false);
const [crashReports, setCrashReports] = React.useState(false);
const [usageData, setUsageData] = React.useState(false);
React.useEffect(() => {
let active = true;
void load("user_id").then(async (id) => {
if (!active) {
return;
}
createEffect(() => {
load("user_id").then(async (id) => {
// For non-logged in users
if (id === 0) {
setPPandToSDone(true);
setDoneWithAnalytics(true);
@ -38,42 +52,63 @@ export default function Screen(props: { children: JSX.Element }) {
const current = await fetch("https://legal.tensamin.net/api/current")
.then((res) => res.json())
.catch((err) => {
if (!active) {
return undefined;
}
setError("Failed to load legal documents");
setErrorDescription(
"An error occurred while fetching the legal documents from the server. Please try again later.",
);
log(0, "Legal", "red", "Failed to fetch legal documents", err);
return undefined;
});
const safeCurrent = legalDocsSchema.safeParse(current);
setRemoteDocs(safeCurrent.data);
if (!active || current === undefined) {
return;
}
const safeCurrent = legalDocsSchema.safeParse(current);
if (!safeCurrent.success) {
setError("Failed to load legal documents");
setErrorDescription(
"The legal documents data received from the server is invalid. Please try again later.",
);
log(
0,
"Legal",
"red",
"Invalid legal documents data",
safeCurrent.error,
);
log(0, "Legal", "red", "Invalid legal documents data", safeCurrent.error);
return;
}
setRemoteDocs(safeCurrent.data);
const localDocs = await load("legal_docs");
await Promise.all([
load("ppandtos_done").then(setPPandToSDone),
load("accepted_privacy_policy").then(acceptPP),
load("accepted_terms_of_service").then(acceptTOS),
load("analytics_done").then(setDoneWithAnalytics),
load("analytics_crash_reports").then(setCrashReports),
load("analytics_usage_data").then(setUsageData),
const [
loadedPPAndTOS,
loadedAcceptedPP,
loadedAcceptedTOS,
loadedAnalyticsDone,
loadedCrashReports,
loadedUsageData,
] = await Promise.all([
load("ppandtos_done"),
load("accepted_privacy_policy"),
load("accepted_terms_of_service"),
load("analytics_done"),
load("analytics_crash_reports"),
load("analytics_usage_data"),
]);
if (!active) {
return;
}
setPPandToSDone(loadedPPAndTOS);
acceptPP(loadedAcceptedPP);
acceptTOS(loadedAcceptedTOS);
setDoneWithAnalytics(loadedAnalyticsDone);
setCrashReports(loadedCrashReports);
setUsageData(loadedUsageData);
if (localDocs.pp.hash !== safeCurrent.data.pp.hash) {
acceptPP(false);
setPPandToSDone(false);
@ -86,87 +121,88 @@ export default function Screen(props: { children: JSX.Element }) {
setLoading(false);
});
});
const [PPandToSDone, setPPandToSDone] = createSignal(false);
const [acceptedPP, acceptPP] = createSignal(false);
const [acceptedTOS, acceptTOS] = createSignal(false);
return () => {
active = false;
};
}, [load]);
const [doneWithAnalytics, setDoneWithAnalytics] = createSignal(false);
const [crashReports, setCrashReports] = createSignal(false);
const [usageData, setUsageData] = createSignal(false);
if (error !== "" && errorDescription !== "") {
return <ErrorScreen error={error} description={errorDescription} />;
}
if (loading) {
return null;
}
if (PPandToSDone && doneWithAnalytics) {
return <>{props.children}</>;
}
return (
<Show
when={error() !== "" && errorDescription() !== ""}
fallback={
<Show when={!loading()} fallback={null}>
<Show
when={!PPandToSDone() || !doneWithAnalytics()}
fallback={props.children}
>
<div class="w-full h-full flex items-center justify-center">
<div class="h-full flex flex-col gap-15 p-10 md:p-40 w-full lg:w-2/3">
{!PPandToSDone() ? (
<div className="w-full h-full flex items-center justify-center">
<div className="h-full flex flex-col gap-15 p-10 md:p-40 w-full lg:w-2/3">
{!PPandToSDone ? (
<>
<h1 class="text-3xl md:text-4xl font-bold">
<h1 className="text-3xl md:text-4xl font-bold">
Privacy Policy & ToS
<p class="text-muted-foreground text-[20px] font-normal pt-3">
{remoteDocs()?.pp.version} / {remoteDocs()?.tos.version}
<p className="text-muted-foreground text-[20px] font-normal pt-3">
{remoteDocs?.pp.version} / {remoteDocs?.tos.version}
</p>
</h1>
<div class="w-full h-full flex flex-col items-center justify-center gap-5">
<div class="justify-start items-start flex flex-col gap-2">
<div className="w-full h-full flex flex-col items-center justify-center gap-5">
<div className="justify-start items-start flex flex-col gap-2">
<BigCheckbox
checked={acceptedPP()}
checked={acceptedPP}
onChange={acceptPP}
label="I agree to the Privacy Policy"
/>
<BigCheckbox
checked={acceptedTOS()}
checked={acceptedTOS}
onChange={acceptTOS}
label="I agree to the Terms of Service"
/>
<div class="w-full border-t-2" />
<div className="w-full border-t-2" />
<Link
label="Privacy Policy"
link={`https://legal.tensamin.net/pp/${remoteDocs()?.pp.version}`}
link={`https://legal.tensamin.net/pp/${remoteDocs?.pp.version}`}
/>
<Link
label="Terms of Service"
link={`https://legal.tensamin.net/tos/${remoteDocs()?.tos.version}`}
link={`https://legal.tensamin.net/tos/${remoteDocs?.tos.version}`}
/>
</div>
</div>
<ContinueButton
disabled={!acceptedPP() || !acceptedTOS()}
disabled={!acceptedPP || !acceptedTOS}
onClick={() => {
const currentDocs = remoteDocs()!;
const currentDocs = remoteDocs;
if (!currentDocs) {
return;
}
save("accepted_privacy_policy", true).then(() =>
save("accepted_terms_of_service", true).then(() =>
save("ppandtos_done", true).then(() =>
save("legal_docs", currentDocs).then(() =>
setPPandToSDone(true),
),
),
),
);
void (async () => {
await save("accepted_privacy_policy", true);
await save("accepted_terms_of_service", true);
await save("ppandtos_done", true);
await save("legal_docs", currentDocs);
setPPandToSDone(true);
})();
}}
/>
</>
) : !doneWithAnalytics() ? (
) : (
<>
<h1 class="text-3xl md:text-4xl font-bold">Analytics</h1>
<div class="w-full h-full flex justify-center items-center">
<div class="justify-start flex flex-col gap-2">
<h1 className="text-3xl md:text-4xl font-bold">Analytics</h1>
<div className="w-full h-full flex justify-center items-center">
<div className="justify-start flex flex-col gap-2">
<BigCheckbox
checked={crashReports()}
checked={crashReports}
onChange={setCrashReports}
label="Send anonymous crash reports"
/>
<BigCheckbox
checked={usageData()}
checked={usageData}
onChange={setUsageData}
label="Send anonymous usage data"
/>
@ -174,44 +210,33 @@ export default function Screen(props: { children: JSX.Element }) {
</div>
<ContinueButton
onClick={() => {
const currentCrashReports = crashReports();
const currentUsageData = usageData();
const currentCrashReports = crashReports;
const currentUsageData = usageData;
save(
"analytics_crash_reports",
currentCrashReports,
).then(() => {
void save("analytics_crash_reports", currentCrashReports).then(() => {
setCrashReports(currentCrashReports);
});
save("analytics_usage_data", currentUsageData).then(
() => {
void save("analytics_usage_data", currentUsageData).then(() => {
setUsageData(currentUsageData);
},
);
save("analytics_done", true).then(() => {
});
void save("analytics_done", true).then(() => {
setDoneWithAnalytics(true);
});
}}
/>
</>
) : null}
)}
</div>
</div>
</Show>
</Show>
}
>
<ErrorScreen error={error()} description={errorDescription()} />
</Show>
);
}
function ContinueButton(props: { onClick: () => void; disabled?: boolean }) {
return (
<div class="w-full flex justify-end">
<div className="w-full flex justify-end">
<Button
size="lg"
class="text-lg w-full md:w-auto"
className="text-lg w-full md:w-auto"
onClick={props.onClick}
disabled={props.disabled}
>
@ -230,10 +255,10 @@ export function BigCheckbox(props: {
<Checkbox
checked={props.checked}
onChange={props.onChange}
class="flex items-center space-x-2"
className="flex items-center space-x-2"
>
<CheckboxControl class="size-5.5 rounded-md flex items-center justify-center" />
<CheckboxLabel class="text-lg">{props.label}</CheckboxLabel>
<CheckboxControl className="size-5.5 rounded-md flex items-center justify-center" />
<CheckboxLabel className="text-lg">{props.label}</CheckboxLabel>
</Checkbox>
);
}

View file

@ -1,25 +1,30 @@
import { createEffect } from "solid-js";
import { render } from "solid-js/web";
import { Route, Router } from "@solidjs/router";
import * as React from "react";
import { createRoot } from "react-dom/client";
import {
Outlet,
RouterProvider,
createRootRoute,
createRoute,
createRouter,
} from "@tanstack/react-router";
import "./index.css";
import NotFound from "@/routes/404";
// Layouts
import RootLayout from "./routes/layout";
import AppLayout from "./routes/app/layout";
// Pages
import Home from "@/routes/app/home";
import ChatScreen from "@tensamin/chat/screen";
import ChatContext from "@tensamin/chat/context";
import Login from "@/routes/screens/login";
import Signup from "@/routes/screens/signup";
import { getMessages } from "@tensamin/chat/behaviour-conversation";
const wrapper = document.getElementById("root");
// Render
const wrapper = document.getElementById("root")!;
if (!wrapper) {
throw new Error("Missing root element");
}
// @ts-expect-error Declare global function
window.setLogLevelToMax = () => {
@ -27,67 +32,101 @@ window.setLogLevelToMax = () => {
location.reload();
};
render(
() => (
<Router
root={(props) => {
// Theme Detection
createEffect(() => {
function RootShell() {
React.useEffect(() => {
try {
const mediaQuery = window.matchMedia(
"(prefers-color-scheme: dark)",
);
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
// Initial check
document.documentElement.classList.toggle(
"dark",
mediaQuery.matches,
);
document.documentElement.classList.toggle("dark", mediaQuery.matches);
// Listen to changes
const handleChange = (e: MediaQueryListEvent) => {
document.documentElement.classList.toggle("dark", e.matches);
const handleChange = (event: MediaQueryListEvent) => {
document.documentElement.classList.toggle("dark", event.matches);
};
mediaQuery.addEventListener("change", handleChange);
// Cleanup
return () => mediaQuery.removeEventListener("change", handleChange);
return () => {
mediaQuery.removeEventListener("change", handleChange);
};
} catch {
/* theme detection not supported */
return;
}
});
}, []);
return (
<div class="w-screen h-screen overflow-hidden">{props.children}</div>
<div className="w-screen h-screen overflow-hidden">
<RootLayout>
<Outlet />
</RootLayout>
</div>
);
}}
>
<Route path="/" component={RootLayout}>
{/* App */}
<Route path="/" component={AppLayout}>
<Route path="/" component={Home} />
<Route path="/chat" component={Chat} />
</Route>
}
{/* Screens */}
<Route path="/">
<Route path="/login" component={Login} />
<Route path="/signup" component={Signup} />
</Route>
</Route>
{/* 404 */}
<Route path="*" component={NotFound} />
</Router>
),
wrapper,
function AppShell() {
return (
<AppLayout>
<Outlet />
</AppLayout>
);
}
function Chat() {
return (
<ChatContext getMessages={getMessages}>
<ChatContext>
<ChatScreen />
</ChatContext>
);
}
const rootRoute = createRootRoute({
component: RootShell,
});
const appRoute = createRoute({
getParentRoute: () => rootRoute,
id: "app",
component: AppShell,
});
const homeRoute = createRoute({
getParentRoute: () => appRoute,
path: "/",
component: Home,
});
const chatRoute = createRoute({
getParentRoute: () => appRoute,
path: "chat",
component: Chat,
});
const loginRoute = createRoute({
getParentRoute: () => rootRoute,
path: "login",
component: Login,
});
const signupRoute = createRoute({
getParentRoute: () => rootRoute,
path: "signup",
component: Signup,
});
const routeTree = rootRoute.addChildren([
appRoute.addChildren([homeRoute, chatRoute]),
loginRoute,
signupRoute,
]);
const router = createRouter({
routeTree,
defaultNotFoundComponent: NotFound,
});
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
createRoot(wrapper).render(<RouterProvider router={router} />);

View file

@ -1,6 +1,6 @@
export default function Page() {
return (
<div class="w-full h-full flex items-center justify-center text-4xl font-bold">
<div className="w-full h-full flex items-center justify-center text-4xl font-bold">
Page not found (404)
</div>
);

View file

@ -1,21 +1,21 @@
import Socket from "@tensamin/ttp/context";
import User from "@tensamin/core-user/context";
import type { RouteSectionProps } from "@solidjs/router";
import type { ReactNode } from "react";
import Sidebar from "@/components/sidebar";
import Conversation from "@/features/conversation/context";
import Navbar from "@/components/navbar";
export default function Layout(props: RouteSectionProps) {
export default function Layout(props: { children: ReactNode }) {
return (
<Socket>
<User>
<Conversation>
<div class="w-full h-full flex bg-sidebar">
<div className="w-full h-full flex bg-sidebar">
<Sidebar />
<div class="w-full h-full flex flex-col">
<div className="w-full h-full flex flex-col">
<Navbar />
<div class="bg-background h-full w-full rounded-tl-3xl border-t border-l">
<div className="bg-background h-full w-full rounded-tl-3xl border-t border-l">
{props.children}
</div>
</div>

View file

@ -1,4 +1,4 @@
import type { RouteSectionProps } from "@solidjs/router";
import type { ReactNode } from "react";
import Storage from "@tensamin/core-storage/context";
import Crypto from "@tensamin/core-crypto/context";
@ -7,7 +7,7 @@ import LegalWrapper from "@/features/legal/screen";
import { Toaster } from "@tensamin/ui/sonner";
export default function Layout(props: RouteSectionProps) {
export default function Layout(props: { children: ReactNode }) {
return (
<>
<Toaster />

View file

@ -1,15 +1,15 @@
import Form from "@/components/screens/login/form";
import { Button } from "@tensamin/ui/button";
import { A } from "@solidjs/router";
import { Link } from "@tanstack/react-router";
export default function Page() {
return (
<div class="w-full h-full flex flex-col gap-10 items-center justify-center">
<div className="w-full h-full flex flex-col gap-10 items-center justify-center">
<Form />
<div class="flex">
<A href="/signup">
<div className="flex">
<Link to="/signup">
<Button variant="outline">Sign up</Button>
</A>
</Link>
</div>
</div>
);

View file

@ -14,8 +14,7 @@
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsx": "react-jsx",
/* Linting */
"strict": true,

View file

@ -1,11 +1,10 @@
import { defineConfig } from "vite";
import solid from "vite-plugin-solid";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
import tailwindcss from "@tailwindcss/vite";
import lucidePreprocess from "vite-plugin-lucide-preprocess";
export default defineConfig({
plugins: [solid(), tsconfigPaths(), tailwindcss(), lucidePreprocess()],
plugins: [react(), tsconfigPaths(), tailwindcss()],
worker: {
format: "es",
},