From 8392dbe724ff0061e952924df79377cea4a0c378 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Tue, 15 Sep 2026 17:40:39 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20FE=20solid-ui=20(shadcn=20port)=20?= =?UTF-8?q?=E2=80=94=20Tailwind=20+=2014=20components,=20all=20pages=20rew?= =?UTF-8?q?ritten?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + web/package.json | 13 +- web/pnpm-lock.yaml | 806 ++++++++++++++++++++++++++- web/postcss.config.js | 6 + web/src/components/Cards.tsx | 70 ++- web/src/components/Citations.tsx | 15 +- web/src/components/WatchlistChat.tsx | 48 +- web/src/components/auth.tsx | 26 +- web/src/components/ui.tsx | 122 ++-- web/src/components/ui/avatar.tsx | 52 ++ web/src/components/ui/badge.tsx | 49 ++ web/src/components/ui/button.tsx | 54 ++ web/src/components/ui/card.tsx | 44 ++ web/src/components/ui/dialog.tsx | 142 +++++ web/src/components/ui/label.tsx | 20 + web/src/components/ui/progress.tsx | 35 ++ web/src/components/ui/separator.tsx | 30 + web/src/components/ui/skeleton.tsx | 25 + web/src/components/ui/switch.tsx | 78 +++ web/src/components/ui/table.tsx | 71 +++ web/src/components/ui/tabs.tsx | 88 +++ web/src/components/ui/text-field.tsx | 153 +++++ web/src/components/ui/tooltip.tsx | 36 ++ web/src/index.tsx | 94 ++-- web/src/lib/utils.ts | 7 + web/src/pages/Alerts.tsx | 156 +++--- web/src/pages/Dashboard.tsx | 74 +-- web/src/pages/Portfolio.tsx | 88 +-- web/src/pages/Report.tsx | 83 +-- web/src/pages/Routines.tsx | 96 ++-- web/src/pages/Screener.tsx | 105 ++-- web/src/styles/globals.css | 68 +++ web/src/styles/tokens.css | 135 ----- web/tailwind.config.ts | 42 ++ 34 files changed, 2390 insertions(+), 542 deletions(-) create mode 100644 web/postcss.config.js create mode 100644 web/src/components/ui/avatar.tsx create mode 100644 web/src/components/ui/badge.tsx create mode 100644 web/src/components/ui/button.tsx create mode 100644 web/src/components/ui/card.tsx create mode 100644 web/src/components/ui/dialog.tsx create mode 100644 web/src/components/ui/label.tsx create mode 100644 web/src/components/ui/progress.tsx create mode 100644 web/src/components/ui/separator.tsx create mode 100644 web/src/components/ui/skeleton.tsx create mode 100644 web/src/components/ui/switch.tsx create mode 100644 web/src/components/ui/table.tsx create mode 100644 web/src/components/ui/tabs.tsx create mode 100644 web/src/components/ui/text-field.tsx create mode 100644 web/src/components/ui/tooltip.tsx create mode 100644 web/src/lib/utils.ts create mode 100644 web/src/styles/globals.css delete mode 100644 web/src/styles/tokens.css create mode 100644 web/tailwind.config.ts diff --git a/.gitignore b/.gitignore index 392ed17..51b6eca 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ .claude-flow/logs/ .claude-flow/sessions/ .hermes/ +.planning # Ruflo runtime artifacts (not source) .swarm/ .claude/ diff --git a/web/package.json b/web/package.json index 4b18d08..95b9bb2 100644 --- a/web/package.json +++ b/web/package.json @@ -9,12 +9,21 @@ "preview": "vite preview" }, "dependencies": { - "solid-js": "1.9.15", + "@kobalte/core": "^0.13.14", "@solidjs/router": "0.15.3", + "autoprefixer": "^10.6.0", "chart.js": "4.4.7", - "solid-chartjs": "1.3.11" + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "postcss": "^8.5.28", + "solid-chartjs": "1.3.11", + "solid-js": "1.9.15", + "tailwind-merge": "^3.7.0", + "tailwindcss": "^3.4.19", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { + "@types/node": "^26.5.1", "typescript": "5.6.3", "vite": "6.4.3", "vite-plugin-solid": "2.11.6" diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 5f88e16..9cc1734 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,31 +8,62 @@ importers: .: dependencies: + '@kobalte/core': + specifier: ^0.13.14 + version: 0.13.14(solid-js@1.9.15) '@solidjs/router': specifier: 0.15.3 version: 0.15.3(solid-js@1.9.15) + autoprefixer: + specifier: ^10.6.0 + version: 10.6.0(postcss@8.5.28) chart.js: specifier: 4.4.7 version: 4.4.7 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + postcss: + specifier: ^8.5.28 + version: 8.5.28 solid-chartjs: specifier: 1.3.11 version: 1.3.11(chart.js@4.4.7)(solid-js@1.9.15) solid-js: specifier: 1.9.15 version: 1.9.15 + tailwind-merge: + specifier: ^3.7.0 + version: 3.7.0 + tailwindcss: + specifier: ^3.4.19 + version: 3.4.19 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.19) devDependencies: + '@types/node': + specifier: ^26.5.1 + version: 26.5.1 typescript: specifier: 5.6.3 version: 5.6.3 vite: specifier: 6.4.3 - version: 6.4.3 + version: 6.4.3(@types/node@26.5.1)(jiti@1.21.7) vite-plugin-solid: specifier: 2.11.6 - version: 2.11.6(solid-js@1.9.15)(vite@6.4.3) + version: 2.11.6(solid-js@1.9.15)(vite@6.4.3(@types/node@26.5.1)(jiti@1.21.7)) packages: + '@alloc/quick-lru@5.3.0': + resolution: {integrity: sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==} + engines: {node: '>=10'} + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} @@ -114,6 +145,11 @@ packages: resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} + '@corvu/utils@0.4.2': + resolution: {integrity: sha512-Ox2kYyxy7NoXdKWdHeDEjZxClwzO4SKM8plAaVwmAJPxHMqA0rLOoAsa+hBDwRLpctf+ZRnAd/ykguuJidnaTA==} + peerDependencies: + solid-js: ^1.8 + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -270,6 +306,18 @@ packages: cpu: [x64] os: [win32] + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@internationalized/number@3.6.8': + resolution: {integrity: sha512-8UmMFia46DUt+k97zKd9fKWXcWHR+k8ae3eYzILETuT2KbIvLyOfac7zesw+sJdRAAZ7Q9pM1Mk22aXp2LD0Ig==} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -286,6 +334,16 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@kobalte/core@0.13.14': + resolution: {integrity: sha512-SBVB1lnvYbYt2x3t7fG0lo5fGLEbHQ6PII3NDPva8vD5e2v53On84ZVQiuIk0XI/55R4GQhden7Yem+R/XR8uQ==} + peerDependencies: + solid-js: ^1.9.8 + + '@kobalte/utils@0.9.2': + resolution: {integrity: sha512-jRVXr+zsVHxzDXRoh+CDeXzvCsFJ6uiHhqqNQ26Cw9ZsZ3D6nqPUBt1gGVtj2ZPmRL3a9Uk1v8D1aJ8/I12Dow==} + peerDependencies: + solid-js: ^1.8.8 + '@kurkle/color@0.3.4': resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} @@ -296,6 +354,18 @@ packages: os: [linux] libc: [glibc] + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + '@rollup/rollup-android-arm-eabi@4.63.2': resolution: {integrity: sha512-Xa6RDoWa+hNiX6PgsljlH6W75RaONx3y6PVlbLhkEWW+GaPQ3dP5gwbL/erAzQHWwkvW5UxdD5l87Qx2FAQ/4A==} cpu: [arm] @@ -434,11 +504,69 @@ packages: cpu: [x64] os: [win32] + '@solid-primitives/event-listener@2.4.6': + resolution: {integrity: sha512-5I0YJcTVYIWoMmgBSROBZGcz+ymhew/pGTg2dHW74BUjFKsV8Li4bOZYl0YAGP4mHw5o4UBd9/BEesqBci3wxw==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/keyed@1.5.3': + resolution: {integrity: sha512-zNadtyYBhJSOjXtogkGHmRxjGdz9KHc8sGGVAGlUABkE8BED2tbIZoxkwSqzOwde8OcUEH0bb5DLZUWIMvyBSA==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/map@0.4.13': + resolution: {integrity: sha512-B1zyFbsiTQvqPr+cuPCXO72sRuczG9Swncqk5P74NCGw1VE8qa/Ry9GlfI1e/VdeQYHjan+XkbE3rO2GW/qKew==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/media@2.3.6': + resolution: {integrity: sha512-pk49gPOq/UMRUJ+pTSrOfBiR8xJjRYHXIf1iR/jSnyQ/KroU+ZXhkZzavC7hvfp2vJeOTW5k2/HN0r3Q1VJ7Pw==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/props@3.2.4': + resolution: {integrity: sha512-MXXdvi2TSB6d+0N6ueA/HP1j/Kh9SEc4WdF1ZDMLwPagxW6pIHHSS9xbRO0RjqSVpNP4j0nxCWT1hx3CkJNhNA==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/refs@1.1.4': + resolution: {integrity: sha512-bLjwIs6ZPu8NQnuw04sU3Zc8qKSpbc0umUU/O4SHf6oWOdO4+dHY8vb1T7C4b/Tg103+9WGr6sEE0+NFlbaB/A==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/resize-observer@2.2.0': + resolution: {integrity: sha512-9Fuu/EWBeGj+atGHRJp70HKhdfalmpjwxY8a32NZixdLNmfCJ45AfhLQNr6uOzETbbiMx4iCKlTrJ8KZCHC2Ww==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/rootless@1.5.4': + resolution: {integrity: sha512-TOIZa1VUfVJ+9nkCcRajw3U4t9vBOP1HxX1WHNTbXq32mXwlqTvUnC4CRIilohcryBkT9u2ZkhUDSHRTaGp55g==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/static-store@0.1.4': + resolution: {integrity: sha512-LgtVaVBtB7EbmS4+M0b8xY5Iq6pUWXBsIC4VgtrFKDGDdyCaDt88sHk0fUlx1Enxm/XZnZyLXJABRoa39RjJqA==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/trigger@1.2.4': + resolution: {integrity: sha512-Ju0e+ZOD7hpOp7nptJimvDSZHWFvIvF9iBWMvuwt30smX7c5wmB8Kmc0AdDuv0wOTi06PQ1J5IrP1WJbH2yUBQ==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/utils@6.4.1': + resolution: {integrity: sha512-ISSB5QX1qP2ynrheIpYwc4oKR5Ny4siNuUyf1qZniy+Il+p/PtDB0QK1Dnle8noiHpwRD3gpPdubOC3qI/Zamg==} + peerDependencies: + solid-js: ^1.6.12 + '@solidjs/router@0.15.3': resolution: {integrity: sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw==} peerDependencies: solid-js: ^1.8.6 + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -454,6 +582,26 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/node@26.5.1': + resolution: {integrity: sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g==} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + autoprefixer@10.6.0: + resolution: {integrity: sha512-A26d6qs9kqGgkmImIXMYvXTzqb4Qv7AVgpY1NXzr9Y659J8qHHnLOO/zE8ewIGFMprOolAoRAQYDgNryXIJKBw==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + babel-plugin-jsx-dom-expressions@0.40.10: resolution: {integrity: sha512-lxve6Y02YiZTldB7efKpnbf1BH00XCFZNYYW235jSGsYaJNFtHrYlKV6/O+miHbjqpIr9FTe5+0no4hofAMbfA==} peerDependencies: @@ -473,11 +621,23 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + browserslist@4.28.9: resolution: {integrity: sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + caniuse-lite@1.0.30001810: resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} @@ -485,9 +645,29 @@ packages: resolution: {integrity: sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==} engines: {pnpm: '>=8'} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -500,6 +680,12 @@ packages: supports-color: optional: true + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + electron-to-chromium@1.5.427: resolution: {integrity: sha512-n14zb3FdsChZ2BNobqNHAJMcP3ifFv4paox2LvCrfVAQcqGiSURgbJl+PfMpHVCNFkStnNc+RRVtPBTVW5PDgw==} @@ -507,6 +693,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} @@ -516,6 +706,13 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fastq@1.20.3: + resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -525,22 +722,68 @@ packages: picomatch: optional: true + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + html-entities@2.3.3: resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + is-what@4.1.16: resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} engines: {node: '>=12.13'} + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -554,6 +797,13 @@ packages: engines: {node: '>=6'} hasBin: true + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -561,9 +811,20 @@ packages: resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} engines: {node: '>=12.13'} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.19: resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -573,25 +834,113 @@ packages: resolution: {integrity: sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==} engines: {node: '>=18'} + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + picomatch@4.0.7: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.4: + resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.5.28: resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + read-cache@1.0.2: + resolution: {integrity: sha512-/peqiBB/n07gQGLsWaHho3WfvUyRscw0gYTsEFMhrIe/nWLkYaf5SbKYjGYqtRV3aPwykJgF2VEMo1ac4bnsGA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rollup@4.63.2: resolution: {integrity: sha512-l5eyksV4tPBj6lJyEa37YzIOCSOV7lkZzEHUdpjWZbtD7wTcFYmEYXSgm5bT4vV+dZLb9rBG1W9GROOG4NS4Ew==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -615,6 +964,16 @@ packages: solid-js@1.9.15: resolution: {integrity: sha512-EeiY2xfpZJqPLjXspVEKjAII4yv8NyG//NxZ3IpOFHdUNnnTyL0uJOeS9LWGvA7cFCz5y94cjFwYlmw5Luncsg==} + solid-presence@0.2.0: + resolution: {integrity: sha512-YM92o+jvpzX3XGaD4rLYmq/Kc2ZVh47GSCLEufHBFQQIurvZTs8SoGJxO8BJGNDxBKdcS8F3dYhW1SDXp4BNjA==} + peerDependencies: + solid-js: ^1.8 + + solid-prevent-scroll@0.1.11: + resolution: {integrity: sha512-2PComVCDHaQN/5t7ogEqUYeHasritZKXZ8Sb/VLkl0Web8o9YhEwOo8LSujJEahvMlYCNyKt0zRJeHldOAeWZg==} + peerDependencies: + solid-js: ^1.8 + solid-refresh@0.6.3: resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} peerDependencies: @@ -624,21 +983,66 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwind-merge@3.7.0: + resolution: {integrity: sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA==} + + tailwindcss-animate@1.0.7: + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + + tailwindcss@3.4.19: + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} + engines: {node: '>=14.0.0'} + hasBin: true + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + typescript@5.6.3: resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} engines: {node: '>=14.17'} hasBin: true + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + update-browserslist-db@1.3.3: resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vite-plugin-solid@2.11.6: resolution: {integrity: sha512-Sl5CTqJTGyEeOsmdH6BOgalIZlwH3t4/y0RQuFLMGnvWMBvxb4+lq7x3BSiAw6etf0QexfNJW7HSOO/Qf7pigg==} peerDependencies: @@ -702,6 +1106,8 @@ packages: snapshots: + '@alloc/quick-lru@5.3.0': {} + '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -813,6 +1219,11 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@corvu/utils@0.4.2(solid-js@1.9.15)': + dependencies: + '@floating-ui/dom': 1.8.0 + solid-js: 1.9.15 + '@esbuild/aix-ppc64@0.25.12': optional: true @@ -891,6 +1302,21 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/utils@0.2.12': {} + + '@internationalized/number@3.6.8': + dependencies: + '@swc/helpers': 0.5.23 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -910,11 +1336,45 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.6.0 + '@kobalte/core@0.13.14(solid-js@1.9.15)': + dependencies: + '@floating-ui/dom': 1.8.0 + '@internationalized/number': 3.6.8 + '@kobalte/utils': 0.9.2(solid-js@1.9.15) + '@solid-primitives/props': 3.2.4(solid-js@1.9.15) + '@solid-primitives/resize-observer': 2.2.0(solid-js@1.9.15) + solid-js: 1.9.15 + solid-presence: 0.2.0(solid-js@1.9.15) + solid-prevent-scroll: 0.1.11(solid-js@1.9.15) + + '@kobalte/utils@0.9.2(solid-js@1.9.15)': + dependencies: + '@solid-primitives/event-listener': 2.4.6(solid-js@1.9.15) + '@solid-primitives/keyed': 1.5.3(solid-js@1.9.15) + '@solid-primitives/map': 0.4.13(solid-js@1.9.15) + '@solid-primitives/media': 2.3.6(solid-js@1.9.15) + '@solid-primitives/props': 3.2.4(solid-js@1.9.15) + '@solid-primitives/refs': 1.1.4(solid-js@1.9.15) + '@solid-primitives/utils': 6.4.1(solid-js@1.9.15) + solid-js: 1.9.15 + '@kurkle/color@0.3.4': {} '@napi-rs/lzma-linux-x64-gnu@1.5.1': optional: true + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.3 + '@rollup/rollup-android-arm-eabi@4.63.2': optional: true @@ -990,10 +1450,73 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.63.2': optional: true + '@solid-primitives/event-listener@2.4.6(solid-js@1.9.15)': + dependencies: + '@solid-primitives/utils': 6.4.1(solid-js@1.9.15) + solid-js: 1.9.15 + + '@solid-primitives/keyed@1.5.3(solid-js@1.9.15)': + dependencies: + solid-js: 1.9.15 + + '@solid-primitives/map@0.4.13(solid-js@1.9.15)': + dependencies: + '@solid-primitives/trigger': 1.2.4(solid-js@1.9.15) + solid-js: 1.9.15 + + '@solid-primitives/media@2.3.6(solid-js@1.9.15)': + dependencies: + '@solid-primitives/event-listener': 2.4.6(solid-js@1.9.15) + '@solid-primitives/rootless': 1.5.4(solid-js@1.9.15) + '@solid-primitives/static-store': 0.1.4(solid-js@1.9.15) + '@solid-primitives/utils': 6.4.1(solid-js@1.9.15) + solid-js: 1.9.15 + + '@solid-primitives/props@3.2.4(solid-js@1.9.15)': + dependencies: + '@solid-primitives/utils': 6.4.1(solid-js@1.9.15) + solid-js: 1.9.15 + + '@solid-primitives/refs@1.1.4(solid-js@1.9.15)': + dependencies: + '@solid-primitives/utils': 6.4.1(solid-js@1.9.15) + solid-js: 1.9.15 + + '@solid-primitives/resize-observer@2.2.0(solid-js@1.9.15)': + dependencies: + '@solid-primitives/event-listener': 2.4.6(solid-js@1.9.15) + '@solid-primitives/rootless': 1.5.4(solid-js@1.9.15) + '@solid-primitives/static-store': 0.1.4(solid-js@1.9.15) + '@solid-primitives/utils': 6.4.1(solid-js@1.9.15) + solid-js: 1.9.15 + + '@solid-primitives/rootless@1.5.4(solid-js@1.9.15)': + dependencies: + '@solid-primitives/utils': 6.4.1(solid-js@1.9.15) + solid-js: 1.9.15 + + '@solid-primitives/static-store@0.1.4(solid-js@1.9.15)': + dependencies: + '@solid-primitives/utils': 6.4.1(solid-js@1.9.15) + solid-js: 1.9.15 + + '@solid-primitives/trigger@1.2.4(solid-js@1.9.15)': + dependencies: + '@solid-primitives/utils': 6.4.1(solid-js@1.9.15) + solid-js: 1.9.15 + + '@solid-primitives/utils@6.4.1(solid-js@1.9.15)': + dependencies: + solid-js: 1.9.15 + '@solidjs/router@0.15.3(solid-js@1.9.15)': dependencies: solid-js: 1.9.15 + '@swc/helpers@0.5.23': + dependencies: + tslib: 2.8.1 + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.29.8 @@ -1017,6 +1540,28 @@ snapshots: '@types/estree@1.0.9': {} + '@types/node@26.5.1': + dependencies: + undici-types: 8.9.0 + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + arg@5.0.2: {} + + autoprefixer@10.6.0(postcss@8.5.28): + dependencies: + browserslist: 4.28.9 + caniuse-lite: 1.0.30001810 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.28 + postcss-value-parser: 4.2.0 + babel-plugin-jsx-dom-expressions@0.40.10(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 @@ -1035,6 +1580,12 @@ snapshots: baseline-browser-mapping@2.11.23: {} + binary-extensions@2.3.0: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + browserslist@4.28.9: dependencies: baseline-browser-mapping: 2.11.23 @@ -1043,24 +1594,54 @@ snapshots: node-releases: 2.0.55 update-browserslist-db: 1.3.3(browserslist@4.28.9) + camelcase-css@2.0.1: {} + caniuse-lite@1.0.30001810: {} chart.js@4.4.7: dependencies: '@kurkle/color': 0.3.4 + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + clsx@2.1.1: {} + + commander@4.1.1: {} + convert-source-map@2.0.0: {} + cssesc@3.0.0: {} + csstype@3.2.3: {} debug@4.4.3: dependencies: ms: 2.1.3 + didyoumean@1.2.2: {} + + dlv@1.1.3: {} + electron-to-chromium@1.5.427: {} entities@6.0.1: {} + es-errors@1.3.0: {} + esbuild@0.25.12: optionalDependencies: '@esbuild/aix-ppc64': 0.25.12 @@ -1092,25 +1673,79 @@ snapshots: escalade@3.2.0: {} + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fastq@1.20.3: + dependencies: + reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + fraction.js@5.3.4: {} + fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + gensync@1.0.0-beta.2: {} + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + html-entities@2.3.3: {} + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + is-what@4.1.16: {} + jiti@1.21.7: {} + js-tokens@4.0.0: {} jsesc@3.1.0: {} json5@2.2.3: {} + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -1119,26 +1754,99 @@ snapshots: dependencies: is-what: 4.1.16 + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + ms@2.1.3: {} + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.19: {} node-releases@2.0.55: {} + normalize-path@3.0.0: {} + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + parse5@7.3.0: dependencies: entities: 6.0.1 + path-parse@1.0.7: {} + picocolors@1.1.1: {} + picomatch@2.3.2: {} + picomatch@4.0.7: {} + pirates@4.0.7: {} + + postcss-import@15.1.0(postcss@8.5.28): + dependencies: + postcss: 8.5.28 + postcss-value-parser: 4.2.0 + read-cache: 1.0.2 + resolve: 1.22.12 + + postcss-js@4.1.0(postcss@8.5.28): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.28 + + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.28): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 1.21.7 + postcss: 8.5.28 + + postcss-nested@6.2.0(postcss@8.5.28): + dependencies: + postcss: 8.5.28 + postcss-selector-parser: 6.1.4 + + postcss-selector-parser@6.1.4: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + postcss@8.5.28: dependencies: nanoid: 3.3.19 picocolors: 1.1.1 source-map-js: 1.2.1 + queue-microtask@1.2.3: {} + + read-cache@1.0.2: {} + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + rollup@4.63.2: dependencies: '@types/estree': 1.0.9 @@ -1171,6 +1879,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.63.2 fsevents: 2.3.3 + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + semver@6.3.1: {} seroval-plugins@1.5.6(seroval@1.5.6): @@ -1190,6 +1902,16 @@ snapshots: seroval: 1.5.6 seroval-plugins: 1.5.6(seroval@1.5.6) + solid-presence@0.2.0(solid-js@1.9.15): + dependencies: + '@corvu/utils': 0.4.2(solid-js@1.9.15) + solid-js: 1.9.15 + + solid-prevent-scroll@0.1.11(solid-js@1.9.15): + dependencies: + '@corvu/utils': 0.4.2(solid-js@1.9.15) + solid-js: 1.9.15 + solid-refresh@0.6.3(solid-js@1.9.15): dependencies: '@babel/generator': 7.29.8 @@ -1201,20 +1923,86 @@ snapshots: source-map-js@1.2.1: {} + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.17 + ts-interface-checker: 0.1.13 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwind-merge@3.7.0: {} + + tailwindcss-animate@1.0.7(tailwindcss@3.4.19): + dependencies: + tailwindcss: 3.4.19 + + tailwindcss@3.4.19: + dependencies: + '@alloc/quick-lru': 5.3.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.28 + postcss-import: 15.1.0(postcss@8.5.28) + postcss-js: 4.1.0(postcss@8.5.28) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.28) + postcss-nested: 6.2.0(postcss@8.5.28) + postcss-selector-parser: 6.1.4 + resolve: 1.22.12 + sucrase: 3.35.1 + transitivePeerDependencies: + - tsx + - yaml + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-interface-checker@0.1.13: {} + + tslib@2.8.1: {} + typescript@5.6.3: {} + undici-types@8.9.0: {} + update-browserslist-db@1.3.3(browserslist@4.28.9): dependencies: browserslist: 4.28.9 escalade: 3.2.0 picocolors: 1.1.1 - vite-plugin-solid@2.11.6(solid-js@1.9.15)(vite@6.4.3): + util-deprecate@1.0.2: {} + + vite-plugin-solid@2.11.6(solid-js@1.9.15)(vite@6.4.3(@types/node@26.5.1)(jiti@1.21.7)): dependencies: '@babel/core': 7.29.7 '@types/babel__core': 7.20.5 @@ -1222,12 +2010,12 @@ snapshots: merge-anything: 5.1.7 solid-js: 1.9.15 solid-refresh: 0.6.3(solid-js@1.9.15) - vite: 6.4.3 - vitefu: 1.1.3(vite@6.4.3) + vite: 6.4.3(@types/node@26.5.1)(jiti@1.21.7) + vitefu: 1.1.3(vite@6.4.3(@types/node@26.5.1)(jiti@1.21.7)) transitivePeerDependencies: - supports-color - vite@6.4.3: + vite@6.4.3(@types/node@26.5.1)(jiti@1.21.7): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.7) @@ -1236,10 +2024,12 @@ snapshots: rollup: 4.63.2 tinyglobby: 0.2.17 optionalDependencies: + '@types/node': 26.5.1 fsevents: 2.3.3 + jiti: 1.21.7 - vitefu@1.1.3(vite@6.4.3): + vitefu@1.1.3(vite@6.4.3(@types/node@26.5.1)(jiti@1.21.7)): optionalDependencies: - vite: 6.4.3 + vite: 6.4.3(@types/node@26.5.1)(jiti@1.21.7) yallist@3.1.1: {} diff --git a/web/postcss.config.js b/web/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/web/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/web/src/components/Cards.tsx b/web/src/components/Cards.tsx index 0554597..44fb6ba 100644 --- a/web/src/components/Cards.tsx +++ b/web/src/components/Cards.tsx @@ -1,45 +1,61 @@ import { For, Show } from "solid-js"; import { fmtIDR } from "../lib/api"; import { Citations } from "./Citations"; +import { Badge } from "./ui/badge"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table"; export function AccumulationTable(props: { rows: { ticker: string; net_sum: number; brokers: number }[] }) { return ( -
-

Top accumulation

- - - - - {(r) => } - - -
TickerNetBrokers
{r.ticker}{fmtIDR(r.net_sum)}{r.brokers}
-

No accumulation snapshots yet.

-
+ + + Top accumulation + Net broker accumulation per ticker. + + + + TickerNetBrokers + + + {(r) => {r.ticker}{fmtIDR(r.net_sum)}{r.brokers}} + + +
+

No accumulation snapshots yet.

+
+
); } export function RotationMap(props: { signal?: string; citations?: Parameters[0]["items"] }) { return ( -
-

Sector rotation

-

{props.signal || "No rotation flip detected."}

- -
+ + + Sector rotation + + +

{props.signal || "No rotation flip detected."}

+ +
+
); } export function ActivityFeed(props: { events: { id?: number; ticker?: string; message?: string; date?: string }[] }) { return ( -
-

Activity

- No events yet.

}> -
    - - {(e) =>
  • {e.ticker} — {e.message} {e.date}
  • } -
    -
-
-
+ + + Activity + + + No events yet.

}> +
    + + {(e) =>
  • {e.ticker} {e.message} {e.date}
  • } +
    +
+
+
+
); } diff --git a/web/src/components/Citations.tsx b/web/src/components/Citations.tsx index 86de755..ad33358 100644 --- a/web/src/components/Citations.tsx +++ b/web/src/components/Citations.tsx @@ -1,5 +1,7 @@ import { For } from "solid-js"; import type { Citation } from "../lib/api"; +import { Badge } from "./ui/badge"; +import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; export function Citations(props: { items?: Citation[] | string }) { const list = (): Citation[] => { @@ -10,12 +12,17 @@ export function Citations(props: { items?: Citation[] | string }) { return props.items; }; return ( - + {(c) => ( - - {c.endpoint} {c.ticker || ""} @ {(c.snapshot_at || "").slice(0, 10)}{c.stale ? " · stale" : ""} - + + + + {c.endpoint} {c.ticker || ""} @ {(c.snapshot_at || "").slice(0, 10)}{c.stale ? " · stale" : ""} + + + {c.endpoint} @ {c.snapshot_at}{c.stale ? " (stale)" : ""} + )} diff --git a/web/src/components/WatchlistChat.tsx b/web/src/components/WatchlistChat.tsx index 8c07c4a..e4b7a25 100644 --- a/web/src/components/WatchlistChat.tsx +++ b/web/src/components/WatchlistChat.tsx @@ -1,7 +1,10 @@ import { createResource, createSignal, For, Show } from "solid-js"; import { api } from "../lib/api"; +import { Badge } from "./ui/badge"; +import { Button } from "./ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"; +import { TextField, TextFieldInput } from "./ui/text-field"; -// WatchlistDrawer: global watchlist add/remove backed by /api/watchlist. export function WatchlistDrawer() { const [wl, { refetch }] = createResource(() => api.watchlist()); const [ticker, setTicker] = createSignal(""); @@ -19,17 +22,22 @@ export function WatchlistDrawer() { refetch(); } return ( -
-

Watchlist

- setTicker(e.currentTarget.value)} style={{ width: "90px" }} /> - -

{err()}

-
    {(t) =>
  • {t}
  • }
-
+ + Watchlist + +
+ setTicker(e.currentTarget.value)} /> + +
+

{err()}

+
    + {(t) =>
  • {t}
  • }
    +
+
+
); } -// ChatSidebar: context-aware Q&A backed by POST /api/chat. export function ChatSidebar() { const [msg, setMsg] = createSignal(""); const [log, setLog] = createSignal<{ q: string; a: string }[]>([]); @@ -45,13 +53,19 @@ export function ChatSidebar() { } } return ( -
-

AI Chat

- - {(m) =>

You: {m.q}

{m.a.slice(0, 500)}

} -
- setMsg(e.currentTarget.value)} style={{ width: "220px" }} /> - -
+ + AI Chat + +
+ + {(m) =>

You: {m.q}

{m.a.slice(0, 500)}

} +
+
+
+ setMsg(e.currentTarget.value)} onKeyDown={(e: KeyboardEvent) => { if (e.key === "Enter") send(); }} /> + +
+
+
); } diff --git a/web/src/components/auth.tsx b/web/src/components/auth.tsx index f0f12c6..cd4b3a5 100644 --- a/web/src/components/auth.tsx +++ b/web/src/components/auth.tsx @@ -1,14 +1,15 @@ import { createResource, createSignal, Show } from "solid-js"; import { api, type AuthUser } from "../lib/api"; +import { Button } from "./ui/button"; +import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; -// AuthState: loads /api/auth/me once; exposes login/logout + theme toggle. +// Theme: .dark class on (shadcn convention). Default dark. function theme(): string { - return localStorage.getItem("fs-theme") || - (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"); + return localStorage.getItem("fs-theme") || "dark"; } export function applyTheme(t: string) { - document.documentElement.dataset.theme = t; + document.documentElement.classList.toggle("dark", t === "dark"); localStorage.setItem("fs-theme", t); } applyTheme(theme()); @@ -16,12 +17,12 @@ applyTheme(theme()); export function ThemeToggle() { const [t, setT] = createSignal(theme()); return ( - + ); } @@ -35,13 +36,16 @@ export function useAuth() { export function AuthButton(props: { me: AuthUser | null | undefined; onLogout: () => void }) { return ( Sign in with Google + }> {(u) => ( - - {u().avatar_url ? : null} - {u().name || u().email} - + + + {u().avatar_url ? : null} + {(u().name || u().email || "?").slice(0, 1).toUpperCase()} + + + )} diff --git a/web/src/components/ui.tsx b/web/src/components/ui.tsx index 823f668..18281f4 100644 --- a/web/src/components/ui.tsx +++ b/web/src/components/ui.tsx @@ -1,66 +1,88 @@ -import { For, Show, type JSX } from "solid-js"; +import { For } from "solid-js"; +import type { Citation } from "../lib/api"; +import { Badge } from "./ui/badge"; +import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; -// Shared building blocks: StatCard, EmptyState, Badge, SignalBar, Skeleton. - -export function StatCard(props: { title: string; value: string; sub?: string; children?: JSX.Element }) { +export function Citations(props: { items?: Citation[] | string }) { + const list = (): Citation[] => { + if (!props.items) return []; + if (typeof props.items === "string") { + try { return JSON.parse(props.items); } catch { return []; } + } + return props.items; + }; return ( -
-

{props.title}

-

{props.value}

-

{props.sub}

- {props.children} -
- ); -} - -export function EmptyState(props: { icon: string; title: string; hint?: string; action?: JSX.Element }) { - return ( -
-
{props.icon}
- {props.title} -

{props.hint}

-
{props.action}
-
- ); -} - -export function Skeleton(props: { rows?: number }) { - const n = props.rows ?? 3; - return ( -
- {() =>
} -
+ + + {(c) => ( + + + + {c.endpoint} {c.ticker || ""} @ {(c.snapshot_at || "").slice(0, 10)}{c.stale ? " · stale" : ""} + + + {c.endpoint} @ {c.snapshot_at}{c.stale ? " (stale)" : ""} + + )} + + ); } export function RecBadge(props: { rec: string }) { - const cls = props.rec === "BUY" ? "buy" : props.rec === "HOLD" ? "hold" : "avoid"; - return {props.rec}; + const v = () => + /buy/i.test(props.rec) ? { variant: "success" as const, icon: "▲" } + : /avoid|sell/i.test(props.rec) ? { variant: "error" as const, icon: "▼" } + : { variant: "warning" as const, icon: "●" }; + return {v().icon} {props.rec}; } -// SignalBar renders one -100..+100 signal as a labeled bar. -export function SignalBar(props: { label: string; value: number }) { - const v = Math.max(-100, Math.min(100, props.value)); - const left = v < 0 ? `${50 + v / 2}%` : "50%"; - const width = `${Math.abs(v) / 2}%`; - const cls = v < 0 ? "neg" : v > 0 ? "pos" : ""; +export function BreakdownBars(props: { breakdown: Record }) { + const entries = () => Object.entries(props.breakdown) + .filter(([, v]) => typeof v === "number") + .sort((a, b) => Math.abs(b[1] as number) - Math.abs(a[1] as number)) + .slice(0, 6); return ( -
- {props.label} - - {v} +
+ + {([k, v]) => { + const n = v as number; + const pct = Math.min(100, Math.abs(n)); + return ( +
+ {k} +
+
= 0, "bg-error": n < 0 }} + style={{ width: `${pct}%`, "margin-left": n < 0 ? "auto" : undefined }} + /> +
+ {n.toFixed(1)} +
+ ); + }} +
); } -// BreakdownBars renders a screen/report signal breakdown map as bars. -// Unknown numeric fields are shown; booleans/strings are skipped except flags. -export function BreakdownBars(props: { breakdown: Record }) { - const entries = () => - Object.entries(props.breakdown || {}).filter(([, v]) => typeof v === "number") as [string, number][]; +export function PageHead(props: { title: string; sub?: string }) { return ( - no signals}> - {([k, v]) => } - +
+

{props.title}

+ {props.sub ?

{props.sub}

: null} +
+ ); +} + +export function EmptyState(props: { icon: string; title: string; hint?: string; action?: import("solid-js").JSX.Element }) { + return ( +
+
{props.icon}
+

{props.title}

+ {props.hint ?

{props.hint}

: null} + {props.action} +
); } diff --git a/web/src/components/ui/avatar.tsx b/web/src/components/ui/avatar.tsx new file mode 100644 index 0000000..b84f7d9 --- /dev/null +++ b/web/src/components/ui/avatar.tsx @@ -0,0 +1,52 @@ +import type { ValidComponent } from "solid-js" +import { splitProps } from "solid-js" + +import * as ImagePrimitive from "@kobalte/core/image" +import type { PolymorphicProps } from "@kobalte/core/polymorphic" + +import { cn } from "../../lib/utils" + +type AvatarRootProps = ImagePrimitive.ImageRootProps & { + class?: string | undefined +} + +const Avatar = ( + props: PolymorphicProps> +) => { + const [local, others] = splitProps(props as AvatarRootProps, ["class"]) + return ( + + ) +} + +type AvatarImageProps = ImagePrimitive.ImageImgProps & { + class?: string | undefined +} + +const AvatarImage = ( + props: PolymorphicProps> +) => { + const [local, others] = splitProps(props as AvatarImageProps, ["class"]) + return +} + +type AvatarFallbackProps = + ImagePrimitive.ImageFallbackProps & { class?: string | undefined } + +const AvatarFallback = ( + props: PolymorphicProps> +) => { + const [local, others] = splitProps(props as AvatarFallbackProps, ["class"]) + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } + diff --git a/web/src/components/ui/badge.tsx b/web/src/components/ui/badge.tsx new file mode 100644 index 0000000..052bf40 --- /dev/null +++ b/web/src/components/ui/badge.tsx @@ -0,0 +1,49 @@ +import type { Component, ComponentProps } from "solid-js" +import { splitProps } from "solid-js" + +import type { VariantProps } from "class-variance-authority" +import { cva } from "class-variance-authority" + +import { cn } from "../../lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: "border-transparent bg-primary text-primary-foreground", + secondary: "border-transparent bg-secondary text-secondary-foreground", + outline: "text-foreground", + success: "border-success-foreground bg-success text-success-foreground", + warning: "border-warning-foreground bg-warning text-warning-foreground", + error: "border-error-foreground bg-error text-error-foreground" + } + }, + defaultVariants: { + variant: "default" + } + } +) + +type BadgeProps = ComponentProps<"div"> & + VariantProps & { + round?: boolean + } + +const Badge: Component = (props) => { + const [local, others] = splitProps(props, ["class", "variant", "round"]) + return ( +
+ ) +} + +export type { BadgeProps } +export { Badge, badgeVariants } + diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx new file mode 100644 index 0000000..fc66652 --- /dev/null +++ b/web/src/components/ui/button.tsx @@ -0,0 +1,54 @@ +import type { JSX, ValidComponent } from "solid-js" +import { splitProps } from "solid-js" + +import * 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 { cn } from "../../lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: "border border-input hover:bg-accent hover:text-accent-foreground", + secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline" + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 px-3 text-xs", + lg: "h-11 px-8", + icon: "size-10" + } + }, + defaultVariants: { + variant: "default", + size: "default" + } + } +) + +type ButtonProps = ButtonPrimitive.ButtonRootProps & + VariantProps & { class?: string | undefined; children?: JSX.Element } + +const Button = ( + props: PolymorphicProps> +) => { + const [local, others] = splitProps(props as ButtonProps, ["variant", "size", "class"]) + return ( + + ) +} + +export { Button, buttonVariants } +export type { ButtonProps } + diff --git a/web/src/components/ui/card.tsx b/web/src/components/ui/card.tsx new file mode 100644 index 0000000..dd05c58 --- /dev/null +++ b/web/src/components/ui/card.tsx @@ -0,0 +1,44 @@ +import type { Component, ComponentProps } from "solid-js" +import { splitProps } from "solid-js" + +import { cn } from "../../lib/utils" + +const Card: Component> = (props) => { + const [local, others] = splitProps(props, ["class"]) + return ( +
+ ) +} + +const CardHeader: Component> = (props) => { + const [local, others] = splitProps(props, ["class"]) + return
+} + +const CardTitle: Component> = (props) => { + const [local, others] = splitProps(props, ["class"]) + return ( +

+ ) +} + +const CardDescription: Component> = (props) => { + const [local, others] = splitProps(props, ["class"]) + return

+} + +const CardContent: Component> = (props) => { + const [local, others] = splitProps(props, ["class"]) + return

+} + +const CardFooter: Component> = (props) => { + const [local, others] = splitProps(props, ["class"]) + return
+} + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } + diff --git a/web/src/components/ui/dialog.tsx b/web/src/components/ui/dialog.tsx new file mode 100644 index 0000000..f0e1bf8 --- /dev/null +++ b/web/src/components/ui/dialog.tsx @@ -0,0 +1,142 @@ +import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js" +import { splitProps } from "solid-js" + +import * as DialogPrimitive from "@kobalte/core/dialog" +import type { PolymorphicProps } from "@kobalte/core/polymorphic" + +import { cn } from "../../lib/utils" + +const Dialog = DialogPrimitive.Root +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal: Component = (props) => { + const [, rest] = splitProps(props, ["children"]) + return ( + +
+ {props.children} +
+
+ ) +} + +type DialogOverlayProps = + DialogPrimitive.DialogOverlayProps & { class?: string | undefined } + +const DialogOverlay = ( + props: PolymorphicProps> +) => { + const [, rest] = splitProps(props as DialogOverlayProps, ["class"]) + return ( + + ) +} + +type DialogContentProps = + DialogPrimitive.DialogContentProps & { + class?: string | undefined + children?: JSX.Element + } + +const DialogContent = ( + props: PolymorphicProps> +) => { + const [, rest] = splitProps(props as DialogContentProps, ["class", "children"]) + return ( + + + + {props.children} + + + + + + Close + + + + ) +} + +const DialogHeader: Component> = (props) => { + const [, rest] = splitProps(props, ["class"]) + return ( +
+ ) +} + +const DialogFooter: Component> = (props) => { + const [, rest] = splitProps(props, ["class"]) + return ( +
+ ) +} + +type DialogTitleProps = DialogPrimitive.DialogTitleProps & { + class?: string | undefined +} + +const DialogTitle = ( + props: PolymorphicProps> +) => { + const [, rest] = splitProps(props as DialogTitleProps, ["class"]) + return ( + + ) +} + +type DialogDescriptionProps = + DialogPrimitive.DialogDescriptionProps & { + class?: string | undefined + } + +const DialogDescription = ( + props: PolymorphicProps> +) => { + const [, rest] = splitProps(props as DialogDescriptionProps, ["class"]) + return ( + + ) +} + +export { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription +} + diff --git a/web/src/components/ui/label.tsx b/web/src/components/ui/label.tsx new file mode 100644 index 0000000..0a1a23b --- /dev/null +++ b/web/src/components/ui/label.tsx @@ -0,0 +1,20 @@ +import type { Component, ComponentProps } from "solid-js" +import { splitProps } from "solid-js" + +import { cn } from "../../lib/utils" + +const Label: Component> = (props) => { + const [local, others] = splitProps(props, ["class"]) + return ( +