diff --git a/ui/components.json b/ui/components.json
new file mode 100644
index 0000000..0b6231b
--- /dev/null
+++ b/ui/components.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "src/styles/globals.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "iconLibrary": "lucide",
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "registries": {}
+}
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 6784b9f..b9af1ec 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -8,38 +8,53 @@
"name": "autocoder",
"version": "1.0.0",
"dependencies": {
- "@radix-ui/react-dialog": "^1.1.2",
- "@radix-ui/react-dropdown-menu": "^2.1.2",
- "@radix-ui/react-tooltip": "^1.1.3",
- "@tanstack/react-query": "^5.60.0",
+ "@radix-ui/react-checkbox": "^1.3.3",
+ "@radix-ui/react-dialog": "^1.1.15",
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
+ "@radix-ui/react-label": "^2.1.8",
+ "@radix-ui/react-popover": "^1.1.15",
+ "@radix-ui/react-radio-group": "^1.3.8",
+ "@radix-ui/react-scroll-area": "^1.2.10",
+ "@radix-ui/react-select": "^2.2.6",
+ "@radix-ui/react-separator": "^1.1.8",
+ "@radix-ui/react-slot": "^1.2.4",
+ "@radix-ui/react-switch": "^1.2.6",
+ "@radix-ui/react-tabs": "^1.1.13",
+ "@radix-ui/react-toggle": "^1.1.10",
+ "@radix-ui/react-tooltip": "^1.2.8",
+ "@tanstack/react-query": "^5.72.0",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/xterm": "^6.0.0",
"@xyflow/react": "^12.10.0",
"canvas-confetti": "^1.9.4",
+ "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dagre": "^0.8.5",
- "lucide-react": "^0.460.0",
- "react": "^18.3.1",
- "react-dom": "^18.3.1"
+ "lucide-react": "^0.475.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "tailwind-merge": "^3.4.0"
},
"devDependencies": {
- "@eslint/js": "^9.13.0",
+ "@eslint/js": "^9.19.0",
"@playwright/test": "^1.57.0",
- "@tailwindcss/vite": "^4.0.0-beta.4",
+ "@tailwindcss/vite": "^4.1.0",
"@types/canvas-confetti": "^1.9.0",
"@types/dagre": "^0.7.53",
- "@types/react": "^18.3.12",
- "@types/react-dom": "^18.3.1",
- "@vitejs/plugin-react": "^4.3.3",
- "eslint": "^9.13.0",
- "eslint-plugin-react-hooks": "^5.0.0",
- "eslint-plugin-react-refresh": "^0.4.14",
- "globals": "^15.11.0",
- "tailwindcss": "^4.0.0-beta.4",
- "typescript": "~5.6.2",
- "typescript-eslint": "^8.11.0",
- "vite": "^5.4.10"
+ "@types/node": "^22.12.0",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
+ "@vitejs/plugin-react": "^4.4.0",
+ "eslint": "^9.19.0",
+ "eslint-plugin-react-hooks": "^5.1.0",
+ "eslint-plugin-react-refresh": "^0.4.19",
+ "globals": "^15.14.0",
+ "tailwindcss": "^4.1.0",
+ "tw-animate-css": "^1.4.0",
+ "typescript": "~5.7.3",
+ "typescript-eslint": "^8.23.0",
+ "vite": "^7.3.0"
}
},
"node_modules/@babel/code-frame": {
@@ -325,9 +340,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
- "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
"cpu": [
"ppc64"
],
@@ -338,13 +353,13 @@
"aix"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
- "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
"cpu": [
"arm"
],
@@ -355,13 +370,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
- "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
"cpu": [
"arm64"
],
@@ -372,13 +387,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
- "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
"cpu": [
"x64"
],
@@ -389,13 +404,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
- "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
"cpu": [
"arm64"
],
@@ -406,13 +421,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
- "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
"cpu": [
"x64"
],
@@ -423,13 +438,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
- "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
"cpu": [
"arm64"
],
@@ -440,13 +455,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
- "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
"cpu": [
"x64"
],
@@ -457,13 +472,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
- "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
"cpu": [
"arm"
],
@@ -474,13 +489,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
- "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
"cpu": [
"arm64"
],
@@ -491,13 +506,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
- "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
"cpu": [
"ia32"
],
@@ -508,13 +523,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
- "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
"cpu": [
"loong64"
],
@@ -525,13 +540,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
- "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
"cpu": [
"mips64el"
],
@@ -542,13 +557,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
- "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
"cpu": [
"ppc64"
],
@@ -559,13 +574,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
- "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
"cpu": [
"riscv64"
],
@@ -576,13 +591,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
- "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
"cpu": [
"s390x"
],
@@ -593,13 +608,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
- "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
"cpu": [
"x64"
],
@@ -610,13 +625,30 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
- "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
"cpu": [
"x64"
],
@@ -627,13 +659,30 @@
"netbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
- "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
"cpu": [
"x64"
],
@@ -644,13 +693,30 @@
"openbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
- "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
"cpu": [
"x64"
],
@@ -661,13 +727,13 @@
"sunos"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
- "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
"cpu": [
"arm64"
],
@@ -678,13 +744,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
- "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
"cpu": [
"ia32"
],
@@ -695,13 +761,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
- "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
"cpu": [
"x64"
],
@@ -712,7 +778,7 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@eslint-community/eslint-utils": {
@@ -1027,6 +1093,12 @@
"node": ">=18"
}
},
+ "node_modules/@radix-ui/number": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
+ "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
+ "license": "MIT"
+ },
"node_modules/@radix-ui/primitive": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
@@ -1056,6 +1128,36 @@
}
}
},
+ "node_modules/@radix-ui/react-checkbox": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz",
+ "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-presence": "1.1.5",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-controllable-state": "1.2.2",
+ "@radix-ui/react-use-previous": "1.1.1",
+ "@radix-ui/react-use-size": "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-collection": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
@@ -1082,6 +1184,24 @@
}
}
},
+ "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
@@ -1148,6 +1268,24 @@
}
}
},
+ "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-direction": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
@@ -1277,6 +1415,52 @@
}
}
},
+ "node_modules/@radix-ui/react-label": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz",
+ "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.4"
+ },
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz",
+ "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.4"
+ },
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-menu": {
"version": "2.1.16",
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz",
@@ -1317,6 +1501,79 @@
}
}
},
+ "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
+ "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@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-slot": "1.2.3",
+ "@radix-ui/react-use-controllable-state": "1.2.2",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
@@ -1420,6 +1677,56 @@
}
}
},
+ "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-radio-group": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz",
+ "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.3",
+ "@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-presence": "1.1.5",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-roving-focus": "1.1.11",
+ "@radix-ui/react-use-controllable-state": "1.2.2",
+ "@radix-ui/react-use-previous": "1.1.1",
+ "@radix-ui/react-use-size": "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-roving-focus": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
@@ -1451,7 +1758,81 @@
}
}
},
- "node_modules/@radix-ui/react-slot": {
+ "node_modules/@radix-ui/react-scroll-area": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz",
+ "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/number": "1.1.1",
+ "@radix-ui/primitive": "1.1.3",
+ "@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-presence": "1.1.5",
+ "@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"
+ },
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-select": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
+ "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/number": "1.1.1",
+ "@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-primitive": "2.1.3",
+ "@radix-ui/react-slot": "1.2.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@radix-ui/react-use-controllable-state": "1.2.2",
+ "@radix-ui/react-use-layout-effect": "1.1.1",
+ "@radix-ui/react-use-previous": "1.1.1",
+ "@radix-ui/react-visually-hidden": "1.2.3",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
@@ -1469,6 +1850,154 @@
}
}
},
+ "node_modules/@radix-ui/react-separator": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz",
+ "integrity": "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.4"
+ },
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz",
+ "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.4"
+ },
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slot": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
+ "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-switch": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz",
+ "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.3",
+ "@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-controllable-state": "1.2.2",
+ "@radix-ui/react-use-previous": "1.1.1",
+ "@radix-ui/react-use-size": "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tabs": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz",
+ "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-direction": "1.1.1",
+ "@radix-ui/react-id": "1.1.1",
+ "@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-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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toggle": {
+ "version": "1.1.10",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz",
+ "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-tooltip": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
@@ -1503,6 +2032,24 @@
}
}
},
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
@@ -1588,6 +2135,21 @@
}
}
},
+ "node_modules/@radix-ui/react-use-previous": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
+ "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-use-rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
@@ -2448,32 +3010,34 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/prop-types": {
- "version": "15.7.15",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
- "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
- "devOptional": true,
- "license": "MIT"
- },
- "node_modules/@types/react": {
- "version": "18.3.27",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
- "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
- "devOptional": true,
+ "node_modules/@types/node": {
+ "version": "22.19.7",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz",
+ "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.9",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz",
+ "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "@types/prop-types": "*",
"csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
- "version": "18.3.7",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
- "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
- "devOptional": true,
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
"license": "MIT",
"peerDependencies": {
- "@types/react": "^18.0.0"
+ "@types/react": "^19.2.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
@@ -3014,6 +3578,18 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/class-variance-authority": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://polar.sh/cva"
+ }
+ },
"node_modules/classcat": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
@@ -3082,7 +3658,7 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
"node_modules/d3-color": {
@@ -3263,9 +3839,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
- "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -3273,32 +3849,35 @@
"esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=12"
+ "node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.21.5",
- "@esbuild/android-arm": "0.21.5",
- "@esbuild/android-arm64": "0.21.5",
- "@esbuild/android-x64": "0.21.5",
- "@esbuild/darwin-arm64": "0.21.5",
- "@esbuild/darwin-x64": "0.21.5",
- "@esbuild/freebsd-arm64": "0.21.5",
- "@esbuild/freebsd-x64": "0.21.5",
- "@esbuild/linux-arm": "0.21.5",
- "@esbuild/linux-arm64": "0.21.5",
- "@esbuild/linux-ia32": "0.21.5",
- "@esbuild/linux-loong64": "0.21.5",
- "@esbuild/linux-mips64el": "0.21.5",
- "@esbuild/linux-ppc64": "0.21.5",
- "@esbuild/linux-riscv64": "0.21.5",
- "@esbuild/linux-s390x": "0.21.5",
- "@esbuild/linux-x64": "0.21.5",
- "@esbuild/netbsd-x64": "0.21.5",
- "@esbuild/openbsd-x64": "0.21.5",
- "@esbuild/sunos-x64": "0.21.5",
- "@esbuild/win32-arm64": "0.21.5",
- "@esbuild/win32-ia32": "0.21.5",
- "@esbuild/win32-x64": "0.21.5"
+ "@esbuild/aix-ppc64": "0.27.2",
+ "@esbuild/android-arm": "0.27.2",
+ "@esbuild/android-arm64": "0.27.2",
+ "@esbuild/android-x64": "0.27.2",
+ "@esbuild/darwin-arm64": "0.27.2",
+ "@esbuild/darwin-x64": "0.27.2",
+ "@esbuild/freebsd-arm64": "0.27.2",
+ "@esbuild/freebsd-x64": "0.27.2",
+ "@esbuild/linux-arm": "0.27.2",
+ "@esbuild/linux-arm64": "0.27.2",
+ "@esbuild/linux-ia32": "0.27.2",
+ "@esbuild/linux-loong64": "0.27.2",
+ "@esbuild/linux-mips64el": "0.27.2",
+ "@esbuild/linux-ppc64": "0.27.2",
+ "@esbuild/linux-riscv64": "0.27.2",
+ "@esbuild/linux-s390x": "0.27.2",
+ "@esbuild/linux-x64": "0.27.2",
+ "@esbuild/netbsd-arm64": "0.27.2",
+ "@esbuild/netbsd-x64": "0.27.2",
+ "@esbuild/openbsd-arm64": "0.27.2",
+ "@esbuild/openbsd-x64": "0.27.2",
+ "@esbuild/openharmony-arm64": "0.27.2",
+ "@esbuild/sunos-x64": "0.27.2",
+ "@esbuild/win32-arm64": "0.27.2",
+ "@esbuild/win32-ia32": "0.27.2",
+ "@esbuild/win32-x64": "0.27.2"
}
},
"node_modules/escalade": {
@@ -3758,6 +4337,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
"license": "MIT"
},
"node_modules/js-yaml": {
@@ -4134,18 +4714,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "license": "MIT",
- "dependencies": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- },
- "bin": {
- "loose-envify": "cli.js"
- }
- },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -4157,12 +4725,12 @@
}
},
"node_modules/lucide-react": {
- "version": "0.460.0",
- "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.460.0.tgz",
- "integrity": "sha512-BVtq/DykVeIvRTJvRAgCsOwaGL8Un3Bxh8MbDxMhEWlZay3T4IpEKDEpwt5KZ0KJMHzgm6jrltxlT5eXOWXDHg==",
+ "version": "0.475.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.475.0.tgz",
+ "integrity": "sha512-NJzvVu1HwFVeZ+Gwq2q00KygM1aBhy/ZrhY9FsAgJtpB+E4R7uxRk9M2iKvHa6/vNxZydIB59htha4c2vvwvVg==",
"license": "ISC",
"peerDependencies": {
- "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/magic-string": {
@@ -4425,28 +4993,24 @@
}
},
"node_modules/react": {
- "version": "18.3.1",
- "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
- "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
+ "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT",
- "dependencies": {
- "loose-envify": "^1.1.0"
- },
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
- "version": "18.3.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
- "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT",
"dependencies": {
- "loose-envify": "^1.1.0",
- "scheduler": "^0.23.2"
+ "scheduler": "^0.27.0"
},
"peerDependencies": {
- "react": "^18.3.1"
+ "react": "^19.2.3"
}
},
"node_modules/react-refresh": {
@@ -4581,13 +5145,10 @@
}
},
"node_modules/scheduler": {
- "version": "0.23.2",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
- "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
- "license": "MIT",
- "dependencies": {
- "loose-envify": "^1.1.0"
- }
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
},
"node_modules/semver": {
"version": "6.3.1",
@@ -4658,6 +5219,16 @@
"node": ">=8"
}
},
+ "node_modules/tailwind-merge": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
+ "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
"node_modules/tailwindcss": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
@@ -4715,6 +5286,16 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
+ "node_modules/tw-animate-css": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz",
+ "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Wombosvideo"
+ }
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -4729,9 +5310,9 @@
}
},
"node_modules/typescript": {
- "version": "5.6.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
- "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
+ "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -4766,6 +5347,13 @@
"typescript": ">=4.8.4 <6.0.0"
}
},
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
@@ -4860,21 +5448,24 @@
}
},
"node_modules/vite": {
- "version": "5.4.21",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
- "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
+ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "^0.21.3",
- "postcss": "^8.4.43",
- "rollup": "^4.20.0"
+ "esbuild": "^0.27.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -4883,19 +5474,25 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": "^18.0.0 || >=20.0.0",
- "less": "*",
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
"lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.4.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"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
+ "jiti": {
+ "optional": true
+ },
"less": {
"optional": true
},
@@ -4916,6 +5513,12 @@
},
"terser": {
"optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
}
}
},
diff --git a/ui/package.json b/ui/package.json
index 6226472..f70b9ca 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -12,37 +12,52 @@
"test:e2e:ui": "playwright test --ui"
},
"dependencies": {
- "@radix-ui/react-dialog": "^1.1.2",
- "@radix-ui/react-dropdown-menu": "^2.1.2",
- "@radix-ui/react-tooltip": "^1.1.3",
- "@tanstack/react-query": "^5.60.0",
+ "@radix-ui/react-checkbox": "^1.3.3",
+ "@radix-ui/react-dialog": "^1.1.15",
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
+ "@radix-ui/react-label": "^2.1.8",
+ "@radix-ui/react-popover": "^1.1.15",
+ "@radix-ui/react-radio-group": "^1.3.8",
+ "@radix-ui/react-scroll-area": "^1.2.10",
+ "@radix-ui/react-select": "^2.2.6",
+ "@radix-ui/react-separator": "^1.1.8",
+ "@radix-ui/react-slot": "^1.2.4",
+ "@radix-ui/react-switch": "^1.2.6",
+ "@radix-ui/react-tabs": "^1.1.13",
+ "@radix-ui/react-toggle": "^1.1.10",
+ "@radix-ui/react-tooltip": "^1.2.8",
+ "@tanstack/react-query": "^5.72.0",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/xterm": "^6.0.0",
"@xyflow/react": "^12.10.0",
"canvas-confetti": "^1.9.4",
+ "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dagre": "^0.8.5",
- "lucide-react": "^0.460.0",
- "react": "^18.3.1",
- "react-dom": "^18.3.1"
+ "lucide-react": "^0.475.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "tailwind-merge": "^3.4.0"
},
"devDependencies": {
- "@eslint/js": "^9.13.0",
+ "@eslint/js": "^9.19.0",
"@playwright/test": "^1.57.0",
- "@tailwindcss/vite": "^4.0.0-beta.4",
+ "@tailwindcss/vite": "^4.1.0",
"@types/canvas-confetti": "^1.9.0",
"@types/dagre": "^0.7.53",
- "@types/react": "^18.3.12",
- "@types/react-dom": "^18.3.1",
- "@vitejs/plugin-react": "^4.3.3",
- "eslint": "^9.13.0",
- "eslint-plugin-react-hooks": "^5.0.0",
- "eslint-plugin-react-refresh": "^0.4.14",
- "globals": "^15.11.0",
- "tailwindcss": "^4.0.0-beta.4",
- "typescript": "~5.6.2",
- "typescript-eslint": "^8.11.0",
- "vite": "^5.4.10"
+ "@types/node": "^22.12.0",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
+ "@vitejs/plugin-react": "^4.4.0",
+ "eslint": "^9.19.0",
+ "eslint-plugin-react-hooks": "^5.1.0",
+ "eslint-plugin-react-refresh": "^0.4.19",
+ "globals": "^15.14.0",
+ "tailwindcss": "^4.1.0",
+ "tw-animate-css": "^1.4.0",
+ "typescript": "~5.7.3",
+ "typescript-eslint": "^8.23.0",
+ "vite": "^7.3.0"
}
}
diff --git a/ui/src/App.tsx b/ui/src/App.tsx
index 0483ab7..5794285 100644
--- a/ui/src/App.tsx
+++ b/ui/src/App.tsx
@@ -27,6 +27,9 @@ import { KeyboardShortcutsHelp } from './components/KeyboardShortcutsHelp'
import { getDependencyGraph } from './lib/api'
import { Loader2, Settings, Moon, Sun } from 'lucide-react'
import type { Feature } from './lib/types'
+import { Button } from '@/components/ui/button'
+import { Card, CardContent } from '@/components/ui/card'
+import { Badge } from '@/components/ui/badge'
const STORAGE_KEY = 'autocoder-selected-project'
const DARK_MODE_KEY = 'autocoder-dark-mode'
@@ -256,9 +259,9 @@ function App() {
}
return (
-
+
{/* Header */}
-
+
{/* Logo and Title */}
@@ -289,47 +292,49 @@ function App() {
url={wsState.devServerUrl}
/>
-
+
{/* Ollama Mode Indicator */}
{settings?.ollama_mode && (

-
Ollama
+
Ollama
)}
{/* GLM Mode Badge */}
{settings?.glm_mode && (
-
GLM
-
+
)}
>
)}
{/* Dark mode toggle - always visible */}
-
+
@@ -341,11 +346,11 @@ function App() {
style={{ paddingBottom: debugOpen ? debugPanelHeight + 32 : undefined }}
>
{!selectedProject ? (
-
+
Welcome to AutoCoder
-
+
Select a project from the dropdown above or create a new one to get started.
@@ -381,15 +386,17 @@ function App() {
features.in_progress.length === 0 &&
features.done.length === 0 &&
wsState.agentStatus === 'running' && (
-
-
-
- Initializing Features...
-
-
- The agent is reading your spec and creating features. This may take a moment.
-
-
+
+
+
+
+ Initializing Features...
+
+
+ The agent is reading your spec and creating features. This may take a moment.
+
+
+
)}
{/* View Toggle - only show when there are features */}
@@ -411,7 +418,7 @@ function App() {
hasSpec={hasSpec}
/>
) : (
-
+
{graphData ? (
) : (
-
+
)}
-
+
)}
)}
@@ -461,7 +468,7 @@ function App() {
{/* Spec Creation Chat - for creating spec from empty kanban */}
{showSpecChat && selectedProject && (
-
+
{
@@ -508,14 +515,10 @@ function App() {
)}
{/* Settings Modal */}
- {showSettings && (
- setShowSettings(false)} />
- )}
+ setShowSettings(false)} />
{/* Keyboard Shortcuts Help */}
- {showKeyboardHelp && (
- setShowKeyboardHelp(false)} />
- )}
+ setShowKeyboardHelp(false)} />
{/* Celebration Overlay - shows when a feature is completed by an agent */}
{wsState.celebration && (
diff --git a/ui/src/components/ActivityFeed.tsx b/ui/src/components/ActivityFeed.tsx
index 46a695b..23c64e9 100644
--- a/ui/src/components/ActivityFeed.tsx
+++ b/ui/src/components/ActivityFeed.tsx
@@ -1,6 +1,7 @@
import { Activity } from 'lucide-react'
import { AgentAvatar } from './AgentAvatar'
import type { AgentMascot } from '../lib/types'
+import { Card, CardContent } from '@/components/ui/card'
interface ActivityItem {
agentName: string
@@ -38,8 +39,8 @@ export function ActivityFeed({ activities, maxItems = 5, showHeader = true }: Ac
{showHeader && (
@@ -47,34 +48,36 @@ export function ActivityFeed({ activities, maxItems = 5, showHeader = true }: Ac
{displayedActivities.map((activity) => (
-
-
-
-
-
- {activity.agentName}
-
-
- #{activity.featureId}
-
-
- {formatTimestamp(activity.timestamp)}
-
+
+
+
+
+
+ {activity.agentName}
+
+
+ #{activity.featureId}
+
+
+ {formatTimestamp(activity.timestamp)}
+
+
+
+ {activity.thought}
+
-
- {activity.thought}
-
-
-
+
+
))}
diff --git a/ui/src/components/AddFeatureForm.tsx b/ui/src/components/AddFeatureForm.tsx
index 834022c..529b801 100644
--- a/ui/src/components/AddFeatureForm.tsx
+++ b/ui/src/components/AddFeatureForm.tsx
@@ -1,6 +1,18 @@
import { useState, useId } from 'react'
import { X, Plus, Trash2, Loader2, AlertCircle } from 'lucide-react'
import { useCreateFeature } from '../hooks/useProjects'
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+} from '@/components/ui/dialog'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Textarea } from '@/components/ui/textarea'
+import { Label } from '@/components/ui/label'
+import { Alert, AlertDescription } from '@/components/ui/alert'
interface Step {
id: string
@@ -65,149 +77,135 @@ export function AddFeatureForm({ projectName, onClose }: AddFeatureFormProps) {
const isValid = category.trim() && name.trim() && description.trim()
return (
-
-
e.stopPropagation()}
- >
- {/* Header */}
-
-
- Add Feature
-
-
-
+
+
+
)
}
diff --git a/ui/src/components/AgentAvatar.tsx b/ui/src/components/AgentAvatar.tsx
index 8831016..edb36d6 100644
--- a/ui/src/components/AgentAvatar.tsx
+++ b/ui/src/components/AgentAvatar.tsx
@@ -606,7 +606,7 @@ export function AgentAvatar({ name, state, size = 'md', showName = false }: Agen
{showName && (
-
+
{name}
)}
diff --git a/ui/src/components/AgentCard.tsx b/ui/src/components/AgentCard.tsx
index befe63b..9fdff64 100644
--- a/ui/src/components/AgentCard.tsx
+++ b/ui/src/components/AgentCard.tsx
@@ -3,6 +3,9 @@ import { useState } from 'react'
import { createPortal } from 'react-dom'
import { AgentAvatar } from './AgentAvatar'
import type { ActiveAgent, AgentLogEntry, AgentType } from '../lib/types'
+import { Card, CardContent } from '@/components/ui/card'
+import { Button } from '@/components/ui/button'
+import { Badge } from '@/components/ui/badge'
interface AgentCardProps {
agent: ActiveAgent
@@ -31,22 +34,22 @@ function getStateText(state: ActiveAgent['state']): string {
}
}
-// Get state color
+// Get state color class
function getStateColor(state: ActiveAgent['state']): string {
switch (state) {
case 'success':
- return 'text-neo-done'
+ return 'text-primary'
case 'error':
- return 'text-neo-pending' // Yellow - just pivoting, not a real error
+ return 'text-yellow-600'
case 'struggling':
- return 'text-orange-500' // Orange - working hard, being persistent
+ return 'text-orange-500'
case 'working':
case 'testing':
- return 'text-neo-progress'
+ return 'text-primary'
case 'thinking':
- return 'text-neo-pending'
+ return 'text-yellow-600'
default:
- return 'text-neo-text-secondary'
+ return 'text-muted-foreground'
}
}
@@ -55,14 +58,13 @@ function getAgentTypeBadge(agentType: AgentType): { label: string; className: st
if (agentType === 'testing') {
return {
label: 'TEST',
- className: 'bg-purple-100 text-purple-700 border-purple-300',
+ className: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300',
icon: FlaskConical,
}
}
- // Default to coding
return {
label: 'CODE',
- className: 'bg-blue-100 text-blue-700 border-blue-300',
+ className: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',
icon: Code,
}
}
@@ -74,75 +76,66 @@ export function AgentCard({ agent, onShowLogs }: AgentCardProps) {
const TypeIcon = typeBadge.icon
return (
-
- {/* Agent type badge */}
-
-
-
- {typeBadge.label}
-
-
+
+
+ {/* Agent type badge */}
+
+
+
+ {typeBadge.label}
+
+
- {/* Header with avatar and name */}
-
-
-
-
- {agent.agentName}
+ {/* Header with avatar and name */}
+
+
+
+
+ {agent.agentName}
+
+
+ {getStateText(agent.state)}
+
-
- {getStateText(agent.state)}
-
-
- {/* Log button */}
- {hasLogs && onShowLogs && (
-
- )}
-
-
- {/* Feature info */}
-
-
- Feature #{agent.featureId}
-
-
- {agent.featureName}
-
-
-
- {/* Thought bubble */}
- {agent.thought && (
-
-
-
-
onShowLogs(agent.agentIndex)}
+ title={`View logs (${agent.logs?.length || 0} entries)`}
>
- {agent.thought}
-
+
+
+ )}
+
+
+ {/* Feature info */}
+
+
+ Feature #{agent.featureId}
+
+
+ {agent.featureName}
- )}
-
+
+ {/* Thought bubble */}
+ {agent.thought && (
+
+
+
+
+ {agent.thought}
+
+
+
+ )}
+
+
)
}
@@ -170,91 +163,76 @@ export function AgentLogModal({ agent, logs, onClose }: AgentLogModalProps) {
const getLogColor = (type: AgentLogEntry['type']) => {
switch (type) {
case 'error':
- return 'text-neo-danger'
+ return 'text-destructive'
case 'state_change':
- return 'text-neo-progress'
+ return 'text-primary'
default:
- return 'text-neo-text'
+ return 'text-foreground'
}
}
- // Use portal to render modal at document body level (avoids overflow:hidden issues)
return createPortal(
{
- // Close when clicking backdrop
if (e.target === e.currentTarget) onClose()
}}
>
-
+
{/* Header */}
-
+
-
+
{agent.agentName} Logs
-
+
{typeBadge.label}
-
+
-
+
Feature #{agent.featureId}: {agent.featureName}
-
{/* Log content */}
-
- {logs.length === 0 ? (
-
No logs available
- ) : (
-
- {logs.map((log, idx) => (
+
+
+ {logs.length === 0 ? (
+
No logs available
+ ) : (
+ logs.map((log, idx) => (
-
+
[{new Date(log.timestamp).toLocaleTimeString()}]
{' '}
{log.line}
- ))}
-
- )}
+ ))
+ )}
+
{/* Footer */}
-
+
{logs.length} log entries
-
+
,
document.body
)
diff --git a/ui/src/components/AgentControl.tsx b/ui/src/components/AgentControl.tsx
index 2b58448..c08a1bd 100644
--- a/ui/src/components/AgentControl.tsx
+++ b/ui/src/components/AgentControl.tsx
@@ -9,6 +9,8 @@ import { useNextScheduledRun } from '../hooks/useSchedules'
import { formatNextRun, formatEndTime } from '../lib/timeUtils'
import { ScheduleModal } from './ScheduleModal'
import type { AgentStatus } from '../lib/types'
+import { Button } from '@/components/ui/button'
+import { Badge } from '@/components/ui/badge'
interface AgentControlProps {
projectName: string
@@ -30,18 +32,17 @@ export function AgentControl({ projectName, status }: AgentControlProps) {
const isLoading = startAgent.isPending || stopAgent.isPending
const isRunning = status === 'running' || status === 'paused'
- const isLoadingStatus = status === 'loading' // Status unknown, waiting for WebSocket
+ const isLoadingStatus = status === 'loading'
const isParallel = concurrency > 1
const handleStart = () => startAgent.mutate({
yoloMode,
parallelMode: isParallel,
- maxConcurrency: concurrency, // Always pass concurrency (1-5)
+ maxConcurrency: concurrency,
testingAgentRatio: settings?.testing_agent_ratio,
})
const handleStop = () => stopAgent.mutate()
- // Simplified: either show Start (when stopped/crashed), Stop (when running/paused), or loading spinner
const isStopped = status === 'stopped' || status === 'crashed'
return (
@@ -50,7 +51,7 @@ export function AgentControl({ projectName, status }: AgentControlProps) {
{/* Concurrency slider - visible when stopped */}
{isStopped && (
-
+
setConcurrency(Number(e.target.value))}
disabled={isLoading}
- className="w-16 h-2 accent-[var(--color-neo-primary)] cursor-pointer"
+ className="w-16 h-2 accent-primary cursor-pointer"
title={`${concurrency} concurrent agent${concurrency > 1 ? 's' : ''}`}
aria-label="Set number of concurrent agents"
/>
-
+
{concurrency}x
@@ -70,80 +71,71 @@ export function AgentControl({ projectName, status }: AgentControlProps) {
{/* Show concurrency indicator when running with multiple agents */}
{isRunning && isParallel && (
-
+
- {concurrency}x
-
+ {concurrency}x
+
)}
{/* Schedule status display */}
{nextRun?.is_currently_running && nextRun.next_end && (
-
-
- Running until {formatEndTime(nextRun.next_end)}
-
+
+
+ Running until {formatEndTime(nextRun.next_end)}
+
)}
{!nextRun?.is_currently_running && nextRun?.next_start && (
-
-
- Next: {formatNextRun(nextRun.next_start)}
-
+
+
+ Next: {formatNextRun(nextRun.next_start)}
+
)}
{/* Start/Stop button */}
{isLoadingStatus ? (
-
+
-
+
) : isStopped ? (
-
{isLoading ? (
) : (
)}
-
+
) : (
-
{isLoading ? (
) : (
)}
-
+
)}
{/* Clock button to open schedule modal */}
-
setShowScheduleModal(true)}
- className="neo-btn text-sm py-2 px-3"
title="Manage schedules"
- aria-label="Manage agent schedules"
>
-
+
{/* Schedule Modal */}
diff --git a/ui/src/components/AgentMissionControl.tsx b/ui/src/components/AgentMissionControl.tsx
index 9fae801..53203e9 100644
--- a/ui/src/components/AgentMissionControl.tsx
+++ b/ui/src/components/AgentMissionControl.tsx
@@ -4,6 +4,9 @@ import { AgentCard, AgentLogModal } from './AgentCard'
import { ActivityFeed } from './ActivityFeed'
import { OrchestratorStatusCard } from './OrchestratorStatusCard'
import type { ActiveAgent, AgentLogEntry, OrchestratorStatus } from '../lib/types'
+import { Card, CardContent } from '@/components/ui/card'
+import { Badge } from '@/components/ui/badge'
+import { Button } from '@/components/ui/button'
const ACTIVITY_COLLAPSED_KEY = 'autocoder-activity-collapsed'
@@ -35,7 +38,6 @@ export function AgentMissionControl({
return false
}
})
- // State for log modal
const [selectedAgentForLogs, setSelectedAgentForLogs] = useState(null)
const toggleActivityCollapsed = () => {
@@ -54,18 +56,18 @@ export function AgentMissionControl({
}
return (
-
+
{/* Header */}
setIsExpanded(!isExpanded)}
- className="w-full flex items-center justify-between px-4 py-3 bg-[var(--color-neo-progress)] hover:brightness-105 transition-all"
+ className="w-full flex items-center justify-between px-4 py-3 bg-primary hover:bg-primary/90 transition-colors"
>
-
-
+
+
Mission Control
-
+
{agents.length > 0
? `${agents.length} ${agents.length === 1 ? 'agent' : 'agents'} active`
: orchestratorStatus?.state === 'initializing'
@@ -74,12 +76,12 @@ export function AgentMissionControl({
? 'Complete'
: 'Orchestrating'
}
-
+
{isExpanded ? (
-
+
) : (
-
+
)}
@@ -90,7 +92,7 @@ export function AgentMissionControl({
${isExpanded ? 'max-h-[600px] opacity-100' : 'max-h-0 opacity-0'}
`}
>
-
+
{/* Orchestrator Status Card */}
{orchestratorStatus && (
@@ -98,7 +100,7 @@ export function AgentMissionControl({
{/* Agent Cards Row */}
{agents.length > 0 && (
-
+
{agents.map((agent) => (
0 && (
-
-
+
-
-
+
+
Recent Activity
-
+
({recentActivity.length})
{activityCollapsed ? (
-
+
) : (
-
+
)}
-
+
)}
-
+
{/* Log Modal */}
@@ -155,6 +159,6 @@ export function AgentMissionControl({
onClose={() => setSelectedAgentForLogs(null)}
/>
)}
-
+
)
}
diff --git a/ui/src/components/AgentThought.tsx b/ui/src/components/AgentThought.tsx
index 6c8d1be..df24934 100644
--- a/ui/src/components/AgentThought.tsx
+++ b/ui/src/components/AgentThought.tsx
@@ -1,6 +1,7 @@
import { useMemo, useState, useEffect } from 'react'
import { Brain, Sparkles } from 'lucide-react'
import type { AgentStatus } from '../lib/types'
+import { Card } from '@/components/ui/card'
interface AgentThoughtProps {
logs: Array<{ line: string; timestamp: string }>
@@ -105,38 +106,25 @@ export function AgentThought({ logs, agentStatus }: AgentThoughtProps) {
${shouldShow ? 'opacity-100 max-h-20' : 'opacity-0 max-h-0'}
`}
>
-
+
{/* Brain Icon with subtle glow */}
{isRunning && (
)}
- {/* Thought text with fade transition + shimmer effect when running */}
+ {/* Thought text with fade transition */}
-
+
)}
-
+
)
}
diff --git a/ui/src/components/AssistantChat.tsx b/ui/src/components/AssistantChat.tsx
index 5dcb303..8f438a1 100644
--- a/ui/src/components/AssistantChat.tsx
+++ b/ui/src/components/AssistantChat.tsx
@@ -12,6 +12,8 @@ import { useAssistantChat } from '../hooks/useAssistantChat'
import { ChatMessage as ChatMessageComponent } from './ChatMessage'
import { ConversationHistory } from './ConversationHistory'
import type { ChatMessage } from '../lib/types'
+import { Button } from '@/components/ui/button'
+import { Textarea } from '@/components/ui/textarea'
interface AssistantChatProps {
projectName: string
@@ -167,28 +169,28 @@ export function AssistantChat({
return (
{/* Header with actions and connection status */}
-
+
{/* Action buttons */}
-
-
-
+ setShowHistory(!showHistory)}
- className={`neo-btn neo-btn-ghost p-1.5 ${
- showHistory
- ? 'text-[var(--color-neo-text)] bg-[var(--color-neo-pending)]'
- : 'text-[var(--color-neo-text-secondary)] hover:text-[var(--color-neo-text)]'
- }`}
+ className="h-8 w-8"
title="Conversation history"
>
-
+
{/* History dropdown */}
{connectionStatus === 'connected' ? (
<>
-
- Connected
+
+ Connected
>
) : connectionStatus === 'connecting' ? (
<>
-
- Connecting...
+
+ Connecting...
>
) : (
<>
-
- Disconnected
+
+ Disconnected
>
)}
{/* Messages area */}
-
+
{isLoadingConversation ? (
-
+
) : displayMessages.length === 0 ? (
-
+
{isLoading ? (
@@ -253,12 +255,12 @@ export function AssistantChat({
{/* Loading indicator */}
{isLoading && displayMessages.length > 0 && (
-
-
+
+
@@ -266,33 +268,21 @@ export function AssistantChat({
)}
{/* Input area */}
-
+
-
-
+
Press Enter to send, Shift+Enter for new line
diff --git a/ui/src/components/AssistantFAB.tsx b/ui/src/components/AssistantFAB.tsx
index 813c518..3caa290 100644
--- a/ui/src/components/AssistantFAB.tsx
+++ b/ui/src/components/AssistantFAB.tsx
@@ -3,6 +3,7 @@
*/
import { MessageCircle, X } from 'lucide-react'
+import { Button } from '@/components/ui/button'
interface AssistantFABProps {
onClick: () => void
@@ -11,24 +12,14 @@ interface AssistantFABProps {
export function AssistantFAB({ onClick, isOpen }: AssistantFABProps) {
return (
-
{isOpen ? : }
-
+
)
}
diff --git a/ui/src/components/AssistantPanel.tsx b/ui/src/components/AssistantPanel.tsx
index 5efe624..cb61420 100644
--- a/ui/src/components/AssistantPanel.tsx
+++ b/ui/src/components/AssistantPanel.tsx
@@ -11,6 +11,7 @@ import { X, Bot } from 'lucide-react'
import { AssistantChat } from './AssistantChat'
import { useConversation } from '../hooks/useConversations'
import type { ChatMessage } from '../lib/types'
+import { Button } from '@/components/ui/button'
interface AssistantPanelProps {
projectName: string
@@ -103,45 +104,37 @@ export function AssistantPanel({ projectName, isOpen, onClose }: AssistantPanelP
className={`
fixed right-0 top-0 bottom-0 z-50
w-[400px] max-w-[90vw]
- bg-neo-card
- border-l-4 border-[var(--color-neo-border)]
+ bg-card
+ border-l border-border
transform transition-transform duration-300 ease-out
- flex flex-col
+ flex flex-col shadow-xl
${isOpen ? 'translate-x-0' : 'translate-x-full'}
`}
- style={{ boxShadow: 'var(--shadow-neo-left-lg)' }}
role="dialog"
aria-label="Project Assistant"
aria-hidden={!isOpen}
>
{/* Header */}
-
+
-
+
-
Project Assistant
-
{projectName}
+
Project Assistant
+
{projectName}
-
-
+
{/* Chat area */}
diff --git a/ui/src/components/CelebrationOverlay.tsx b/ui/src/components/CelebrationOverlay.tsx
index 55f9d66..8758e69 100644
--- a/ui/src/components/CelebrationOverlay.tsx
+++ b/ui/src/components/CelebrationOverlay.tsx
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react'
import { Sparkles, PartyPopper } from 'lucide-react'
import { AgentAvatar } from './AgentAvatar'
import type { AgentMascot } from '../lib/types'
+import { Card, CardContent } from '@/components/ui/card'
interface CelebrationOverlayProps {
agentName: AgentMascot | 'Unknown'
@@ -80,17 +81,18 @@ export function CelebrationOverlay({ agentName, featureName, onComplete }: Celeb
{/* Celebration card - click to dismiss */}
-
-
+
{/* Icons */}
{/* Avatar celebrating */}
@@ -98,23 +100,23 @@ export function CelebrationOverlay({ agentName, featureName, onComplete }: Celeb
{/* Message */}
-
+
Feature Complete!
-
+
{featureName}
-
+
Great job, {agentName}!
{/* Dismiss hint */}
-
+
Click or press Esc to dismiss
-
-
+
+
)
}
diff --git a/ui/src/components/ChatMessage.tsx b/ui/src/components/ChatMessage.tsx
index a3417b5..4ca9f10 100644
--- a/ui/src/components/ChatMessage.tsx
+++ b/ui/src/components/ChatMessage.tsx
@@ -2,12 +2,13 @@
* Chat Message Component
*
* Displays a single message in the spec creation chat.
- * Supports user, assistant, and system messages with neobrutalism styling.
+ * Supports user, assistant, and system messages with clean styling.
*/
import { memo } from 'react'
import { Bot, User, Info } from 'lucide-react'
import type { ChatMessage as ChatMessageType } from '../lib/types'
+import { Card } from '@/components/ui/card'
interface ChatMessageProps {
message: ChatMessageType
@@ -25,37 +26,34 @@ export const ChatMessage = memo(function ChatMessage({ message }: ChatMessagePro
minute: '2-digit',
})
- // Role-specific styling using CSS variables for theme consistency
+ // Role-specific styling
const roleConfig = {
user: {
icon: User,
- bgColor: 'bg-[var(--color-neo-pending)]',
- textColor: 'text-[var(--color-neo-text-on-bright)]',
- borderColor: 'border-[var(--color-neo-border)]',
+ bgColor: 'bg-primary',
+ textColor: 'text-primary-foreground',
align: 'justify-end',
bubbleAlign: 'items-end',
- iconBg: 'bg-[var(--color-neo-pending)]',
- shadow: 'var(--shadow-neo-md)',
+ iconBg: 'bg-primary',
+ iconColor: 'text-primary-foreground',
},
assistant: {
icon: Bot,
- bgColor: 'bg-[var(--color-neo-card)]',
- textColor: 'text-[var(--color-neo-text)]',
- borderColor: 'border-[var(--color-neo-border)]',
+ bgColor: 'bg-muted',
+ textColor: 'text-foreground',
align: 'justify-start',
bubbleAlign: 'items-start',
- iconBg: 'bg-[var(--color-neo-progress)]',
- shadow: 'var(--shadow-neo-md)',
+ iconBg: 'bg-secondary',
+ iconColor: 'text-secondary-foreground',
},
system: {
icon: Info,
- bgColor: 'bg-[var(--color-neo-done)]',
- textColor: 'text-[var(--color-neo-text-on-bright)]',
- borderColor: 'border-[var(--color-neo-border)]',
+ bgColor: 'bg-green-100 dark:bg-green-900/30',
+ textColor: 'text-green-900 dark:text-green-100',
align: 'justify-center',
bubbleAlign: 'items-center',
- iconBg: 'bg-[var(--color-neo-done)]',
- shadow: 'var(--shadow-neo-sm)',
+ iconBg: 'bg-green-500',
+ iconColor: 'text-white',
},
}
@@ -66,15 +64,7 @@ export const ChatMessage = memo(function ChatMessage({ message }: ChatMessagePro
if (role === 'system') {
return (
-
+
{content}
@@ -90,28 +80,12 @@ export const ChatMessage = memo(function ChatMessage({ message }: ChatMessagePro
{/* Message bubble */}
{role === 'assistant' && (
-
-
+
+
)}
-
+
{/* Parse content for basic markdown-like formatting */}
{content && (
@@ -152,19 +126,15 @@ export const ChatMessage = memo(function ChatMessage({ message }: ChatMessagePro
{attachments && attachments.length > 0 && (
{attachments.map((attachment) => (
-
+

window.open(attachment.previewUrl, '_blank')}
title={`${attachment.filename} (click to enlarge)`}
/>
-
+
{attachment.filename}
@@ -174,27 +144,19 @@ export const ChatMessage = memo(function ChatMessage({ message }: ChatMessagePro
{/* Streaming indicator */}
{isStreaming && (
-
+
)}
-
+
{role === 'user' && (
-
{/* Timestamp */}
-
+
{timeString}
diff --git a/ui/src/components/ConfirmDialog.tsx b/ui/src/components/ConfirmDialog.tsx
index 6e6a2a5..6d04893 100644
--- a/ui/src/components/ConfirmDialog.tsx
+++ b/ui/src/components/ConfirmDialog.tsx
@@ -1,12 +1,21 @@
/**
* ConfirmDialog Component
*
- * A reusable confirmation dialog following the neobrutalism design system.
+ * A reusable confirmation dialog using ShadCN Dialog components.
* Used to confirm destructive actions like deleting projects.
*/
import type { ReactNode } from 'react'
-import { AlertTriangle, X } from 'lucide-react'
+import { AlertTriangle } from 'lucide-react'
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from '@/components/ui/dialog'
+import { Button } from '@/components/ui/button'
interface ConfirmDialogProps {
isOpen: boolean
@@ -31,74 +40,39 @@ export function ConfirmDialog({
onConfirm,
onCancel,
}: ConfirmDialogProps) {
- if (!isOpen) return null
-
- const variantColors = {
- danger: {
- icon: 'var(--color-neo-danger)',
- button: 'neo-btn-danger',
- },
- warning: {
- icon: 'var(--color-neo-pending)',
- button: 'neo-btn-warning',
- },
- }
-
- const colors = variantColors[variant]
-
return (
-
-
e.stopPropagation()}
- >
- {/* Header */}
-
+
-
- {/* Content */}
-
-
- {message}
-
-
- {/* Actions */}
-
-
- {cancelLabel}
-
-
- {isLoading ? 'Deleting...' : confirmLabel}
-
-
-
-
-
+ {isLoading ? 'Deleting...' : confirmLabel}
+
+
+
+
)
}
diff --git a/ui/src/components/ConversationHistory.tsx b/ui/src/components/ConversationHistory.tsx
index db31c58..cbafe79 100644
--- a/ui/src/components/ConversationHistory.tsx
+++ b/ui/src/components/ConversationHistory.tsx
@@ -10,6 +10,8 @@ import { MessageSquare, Trash2, Loader2, AlertCircle } from 'lucide-react'
import { useConversations, useDeleteConversation } from '../hooks/useConversations'
import { ConfirmDialog } from './ConfirmDialog'
import type { AssistantConversation } from '../lib/types'
+import { Button } from '@/components/ui/button'
+import { Card, CardContent, CardHeader } from '@/components/ui/card'
interface ConversationHistoryProps {
projectName: string
@@ -116,78 +118,73 @@ export function ConversationHistory({
/>
{/* Dropdown */}
-
+
{/* Header */}
-
+
Conversation History
-
+
{/* Content */}
- {isLoading ? (
-
-
-
- ) : !conversations || conversations.length === 0 ? (
-
- No conversations yet
-
- ) : (
-
- {conversations.map((conversation) => {
- const isCurrent = conversation.id === currentConversationId
+
+ {isLoading ? (
+
+
+
+ ) : !conversations || conversations.length === 0 ? (
+
+ No conversations yet
+
+ ) : (
+
+ {conversations.map((conversation) => {
+ const isCurrent = conversation.id === currentConversationId
- return (
-
-
handleSelectConversation(conversation.id)}
- className="flex-1 neo-dropdown-item text-left"
- disabled={isCurrent}
+ return (
+
-
-
-
-
- {conversation.title || 'Untitled conversation'}
-
-
-
{conversation.message_count} messages
-
|
-
{formatRelativeTime(conversation.updated_at)}
+
handleSelectConversation(conversation.id)}
+ className="flex-1 px-3 py-2 text-left"
+ disabled={isCurrent}
+ >
+
+
+
+
+ {conversation.title || 'Untitled conversation'}
+
+
+ {conversation.message_count} messages
+ |
+ {formatRelativeTime(conversation.updated_at)}
+
-
-
-
handleDeleteClick(e, conversation)}
- className={`p-2 mr-2 transition-colors rounded ${
- isCurrent
- ? 'text-[var(--color-neo-text-on-bright)] opacity-60 hover:opacity-100 hover:bg-[var(--color-neo-danger)]/20'
- : 'text-[var(--color-neo-text-secondary)] opacity-0 group-hover:opacity-100 hover:text-[var(--color-neo-danger)] hover:bg-[var(--color-neo-danger)]/10'
- }`}
- title="Delete conversation"
- >
-
-
-
- )
- })}
-
- )}
-
+
+
handleDeleteClick(e, conversation)}
+ className={`h-8 w-8 mr-2 ${
+ isCurrent
+ ? 'opacity-60 hover:opacity-100'
+ : 'opacity-0 group-hover:opacity-100'
+ } hover:text-destructive hover:bg-destructive/10`}
+ title="Delete conversation"
+ >
+
+
+
+ )
+ })}
+
+ )}
+
+
{/* Delete Confirmation Dialog */}
{`Are you sure you want to delete "${conversationToDelete?.title || 'this conversation'}"? This action cannot be undone.`}
-
+
diff --git a/ui/src/components/DebugLogViewer.tsx b/ui/src/components/DebugLogViewer.tsx
index 1492d1f..80b6249 100644
--- a/ui/src/components/DebugLogViewer.tsx
+++ b/ui/src/components/DebugLogViewer.tsx
@@ -12,6 +12,8 @@ import { Terminal } from './Terminal'
import { TerminalTabs } from './TerminalTabs'
import { listTerminals, createTerminal, renameTerminal, deleteTerminal } from '@/lib/api'
import type { TerminalInfo } from '@/lib/types'
+import { Button } from '@/components/ui/button'
+import { Badge } from '@/components/ui/badge'
const MIN_HEIGHT = 150
const MAX_HEIGHT = 600
@@ -273,18 +275,18 @@ export function DebugLogViewer({
return 'info'
}
- // Get color class for log level using theme CSS variables
+ // Get color class for log level
const getLogColor = (level: LogLevel): string => {
switch (level) {
case 'error':
- return 'text-[var(--color-neo-log-error)]'
+ return 'text-red-500'
case 'warn':
- return 'text-[var(--color-neo-log-warning)]'
+ return 'text-yellow-500'
case 'debug':
- return 'text-[var(--color-neo-log-debug)]'
+ return 'text-blue-400'
case 'info':
default:
- return 'text-[var(--color-neo-log-info)]'
+ return 'text-foreground'
}
}
@@ -316,89 +318,83 @@ export function DebugLogViewer({
className="absolute top-0 left-0 right-0 h-2 cursor-ns-resize group flex items-center justify-center -translate-y-1/2 z-50"
onMouseDown={handleResizeStart}
>
-
)}
{/* Header bar */}
{/* Collapse/expand toggle */}
-
-
+
+
Debug
-
+
D
-
+
{/* Tabs - only visible when open */}
{isOpen && (
- {
e.stopPropagation()
setActiveTab('agent')
}}
- className={`flex items-center gap-1.5 px-3 py-1 text-xs font-mono rounded transition-colors ${
- activeTab === 'agent'
- ? 'bg-[var(--color-neo-card)] text-[var(--color-neo-text)]'
- : 'text-[var(--color-neo-text-muted)] hover:text-[var(--color-neo-text)] hover:bg-[var(--color-neo-hover-subtle)]'
- }`}
+ className="h-7 text-xs font-mono gap-1.5"
>
Agent
{logs.length > 0 && (
-
+
{logs.length}
-
+
)}
-
-
+ {
e.stopPropagation()
setActiveTab('devserver')
}}
- className={`flex items-center gap-1.5 px-3 py-1 text-xs font-mono rounded transition-colors ${
- activeTab === 'devserver'
- ? 'bg-[var(--color-neo-card)] text-[var(--color-neo-text)]'
- : 'text-[var(--color-neo-text-muted)] hover:text-[var(--color-neo-text)] hover:bg-[var(--color-neo-hover-subtle)]'
- }`}
+ className="h-7 text-xs font-mono gap-1.5"
>
Dev Server
{devLogs.length > 0 && (
-
+
{devLogs.length}
-
+
)}
-
-
+ {
e.stopPropagation()
setActiveTab('terminal')
}}
- className={`flex items-center gap-1.5 px-3 py-1 text-xs font-mono rounded transition-colors ${
- activeTab === 'terminal'
- ? 'bg-[var(--color-neo-card)] text-[var(--color-neo-text)]'
- : 'text-[var(--color-neo-text-muted)] hover:text-[var(--color-neo-text)] hover:bg-[var(--color-neo-hover-subtle)]'
- }`}
+ className="h-7 text-xs font-mono gap-1.5"
>
Terminal
-
+
T
-
-
+
+
)}
@@ -406,14 +402,14 @@ export function DebugLogViewer({
{isOpen && activeTab !== 'terminal' && (
<>
{getCurrentLogCount() > 0 && (
-
+
{getCurrentLogCount()}
-
+
)}
{isAutoScrollPaused() && (
-
+
Paused
-
+
)}
>
)}
@@ -422,22 +418,24 @@ export function DebugLogViewer({
{/* Clear button - only for log tabs */}
{isOpen && activeTab !== 'terminal' && (
-
{
e.stopPropagation()
handleClear()
}}
- className="p-1.5 hover:bg-[var(--color-neo-hover-subtle)] rounded transition-colors"
+ className="h-7 w-7"
title="Clear logs"
>
-
-
+
+
)}
{isOpen ? (
-
+
) : (
-
+
)}
@@ -445,7 +443,7 @@ export function DebugLogViewer({
{/* Content area */}
{isOpen && (
-
+
{/* Agent Logs Tab */}
{activeTab === 'agent' && (
{logs.length === 0 ? (
-
+
No logs yet. Start the agent to see output.
) : (
@@ -467,9 +465,9 @@ export function DebugLogViewer({
return (
-
+
{timestamp}
@@ -491,7 +489,7 @@ export function DebugLogViewer({
className="h-full overflow-y-auto p-2 font-mono text-sm"
>
{devLogs.length === 0 ? (
-
+
No dev server logs yet.
) : (
@@ -504,9 +502,9 @@ export function DebugLogViewer({
return (
-
+
{timestamp}
@@ -538,11 +536,11 @@ export function DebugLogViewer({
{/* Terminal content - render all terminals and show/hide to preserve buffers */}
{isLoadingTerminals ? (
-
+
Loading terminals...
) : terminals.length === 0 ? (
-
+
No terminal available
) : (
diff --git a/ui/src/components/DependencyBadge.tsx b/ui/src/components/DependencyBadge.tsx
index 48f2e97..143f592 100644
--- a/ui/src/components/DependencyBadge.tsx
+++ b/ui/src/components/DependencyBadge.tsx
@@ -1,5 +1,6 @@
import { AlertTriangle, GitBranch, Check } from 'lucide-react'
import type { Feature } from '../lib/types'
+import { Badge } from '@/components/ui/badge'
interface DependencyBadgeProps {
feature: Feature
@@ -38,14 +39,13 @@ export function DependencyBadge({ feature, allFeatures = [], compact = false }:
if (compact) {
// Compact view for card displays
return (
-
{satisfiedCount}/{dependencies.length}
>
)}
-
+
)
}
@@ -70,15 +70,15 @@ export function DependencyBadge({ feature, allFeatures = [], compact = false }:
return (
{isBlocked ? (
-
+
Blocked by {blockingCount} {blockingCount === 1 ? 'dependency' : 'dependencies'}
) : (
-
-
+
+
All {dependencies.length} {dependencies.length === 1 ? 'dependency' : 'dependencies'} satisfied
@@ -102,7 +102,7 @@ export function DependencyIndicator({ feature }: { feature: Feature }) {
if (isBlocked) {
return (
@@ -112,7 +112,7 @@ export function DependencyIndicator({ feature }: { feature: Feature }) {
return (
diff --git a/ui/src/components/DependencyGraph.tsx b/ui/src/components/DependencyGraph.tsx
index 689892b..3061548 100644
--- a/ui/src/components/DependencyGraph.tsx
+++ b/ui/src/components/DependencyGraph.tsx
@@ -18,6 +18,8 @@ import dagre from 'dagre'
import { CheckCircle2, Circle, Loader2, AlertTriangle, RefreshCw } from 'lucide-react'
import type { DependencyGraph as DependencyGraphData, GraphNode, ActiveAgent, AgentMascot, AgentState } from '../lib/types'
import { AgentAvatar } from './AgentAvatar'
+import { Button } from '@/components/ui/button'
+import { Card, CardContent } from '@/components/ui/card'
import '@xyflow/react/dist/style.css'
// Node dimensions
@@ -69,20 +71,17 @@ class GraphErrorBoundary extends Component
+
-
-
Graph rendering error
-
+
+
Graph rendering error
+
The dependency graph encountered an issue.
-
+
Reload Graph
-
+
)
@@ -95,32 +94,39 @@ class GraphErrorBoundary extends Component
void; agent?: NodeAgentInfo } }) {
const statusColors = {
- pending: 'bg-neo-pending border-neo-border',
- in_progress: 'bg-neo-progress border-neo-border',
- done: 'bg-neo-done border-neo-border',
- blocked: 'bg-neo-danger/20 border-neo-danger',
+ pending: 'bg-yellow-100 border-yellow-300 dark:bg-yellow-900/30 dark:border-yellow-700',
+ in_progress: 'bg-cyan-100 border-cyan-300 dark:bg-cyan-900/30 dark:border-cyan-700',
+ done: 'bg-green-100 border-green-300 dark:bg-green-900/30 dark:border-green-700',
+ blocked: 'bg-red-50 border-red-300 dark:bg-red-900/20 dark:border-red-700',
+ }
+
+ const textColors = {
+ pending: 'text-yellow-900 dark:text-yellow-100',
+ in_progress: 'text-cyan-900 dark:text-cyan-100',
+ done: 'text-green-900 dark:text-green-100',
+ blocked: 'text-red-900 dark:text-red-100',
}
const StatusIcon = () => {
switch (data.status) {
case 'done':
- return
+ return
case 'in_progress':
- return
+ return
case 'blocked':
- return
+ return
default:
- return
+ return
}
}
return (
<>
-
+
void; agent
{/* Agent avatar badge - positioned at top right */}
{data.agent && (
-
)}
-
+
#{data.priority}
{/* Show agent name inline if present */}
{data.agent && (
-
+
{data.agent.name}
)}
-
+
{data.name}
-
-
+
>
)
}
@@ -249,10 +255,10 @@ function DependencyGraphInner({ graphData, onNodeClick, activeAgents = [] }: Dep
target: String(edge.target),
type: 'smoothstep',
animated: false,
- style: { stroke: 'var(--color-neo-border)', strokeWidth: 2 },
+ style: { stroke: '#a1a1aa', strokeWidth: 2 },
markerEnd: {
type: MarkerType.ArrowClosed,
- color: 'var(--color-neo-border)',
+ color: '#a1a1aa',
},
}))
@@ -308,22 +314,22 @@ function DependencyGraphInner({ graphData, onNodeClick, activeAgents = [] }: Dep
const status = (node.data as unknown as GraphNode).status
switch (status) {
case 'done':
- return 'var(--color-neo-done)'
+ return '#22c55e' // green-500
case 'in_progress':
- return 'var(--color-neo-progress)'
+ return '#06b6d4' // cyan-500
case 'blocked':
- return 'var(--color-neo-danger)'
+ return '#ef4444' // red-500
default:
- return 'var(--color-neo-pending)'
+ return '#eab308' // yellow-500
}
}, [])
if (graphData.nodes.length === 0) {
return (
-
+
-
No features to display
-
+
No features to display
+
Create features to see the dependency graph
@@ -332,57 +338,49 @@ function DependencyGraphInner({ graphData, onNodeClick, activeAgents = [] }: Dep
}
return (
-
+
{/* Layout toggle */}
- onLayout('LR')}
- className={`
- px-3 py-1.5 text-sm font-medium rounded border-2 border-neo-border transition-all
- ${direction === 'LR'
- ? 'bg-neo-accent text-white shadow-neo-sm'
- : 'bg-white text-neo-text hover:bg-neo-neutral-100'
- }
- `}
>
Horizontal
-
-
+ onLayout('TB')}
- className={`
- px-3 py-1.5 text-sm font-medium rounded border-2 border-neo-border transition-all
- ${direction === 'TB'
- ? 'bg-neo-accent text-white shadow-neo-sm'
- : 'bg-white text-neo-text hover:bg-neo-neutral-100'
- }
- `}
>
Vertical
-
+
{/* Legend */}
-
-
Status
-
-
-
-
Pending
+
+
+ Status
+
-
-
-
-
-
+
+
-
+
diff --git a/ui/src/components/DevServerControl.tsx b/ui/src/components/DevServerControl.tsx
index a6182c5..188e875 100644
--- a/ui/src/components/DevServerControl.tsx
+++ b/ui/src/components/DevServerControl.tsx
@@ -2,6 +2,7 @@ import { Globe, Square, Loader2, ExternalLink, AlertTriangle } from 'lucide-reac
import { useMutation, useQueryClient } from '@tanstack/react-query'
import type { DevServerStatus } from '../lib/types'
import { startDevServer, stopDevServer } from '../lib/api'
+import { Button } from '@/components/ui/button'
// Re-export DevServerStatus from lib/types for consumers that import from here
export type { DevServerStatus }
@@ -86,14 +87,11 @@ export function DevServerControl({ projectName, status, url }: DevServerControlP
return (
{isStopped ? (
-
@@ -104,16 +102,13 @@ export function DevServerControl({ projectName, status, url }: DevServerControlP
) : (
)}
-
+
) : (
-
@@ -122,31 +117,31 @@ export function DevServerControl({ projectName, status, url }: DevServerControlP
) : (
)}
-
+
)}
{/* Show URL as clickable link when server is running */}
{isRunning && url && (
-
- {url}
-
-
+
+ {url}
+
+
+
)}
{/* Error display */}
{(startDevServerMutation.error || stopDevServerMutation.error) && (
-
+
{String((startDevServerMutation.error || stopDevServerMutation.error)?.message || 'Operation failed')}
)}
diff --git a/ui/src/components/EditFeatureForm.tsx b/ui/src/components/EditFeatureForm.tsx
index 6fcf4a3..1095f0d 100644
--- a/ui/src/components/EditFeatureForm.tsx
+++ b/ui/src/components/EditFeatureForm.tsx
@@ -2,6 +2,18 @@ import { useState, useId } from 'react'
import { X, Save, Plus, Trash2, Loader2, AlertCircle } from 'lucide-react'
import { useUpdateFeature } from '../hooks/useProjects'
import type { Feature } from '../lib/types'
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+} from '@/components/ui/dialog'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Textarea } from '@/components/ui/textarea'
+import { Label } from '@/components/ui/label'
+import { Alert, AlertDescription } from '@/components/ui/alert'
interface Step {
id: string
@@ -83,149 +95,135 @@ export function EditFeatureForm({ feature, projectName, onClose, onSaved }: Edit
JSON.stringify(currentSteps) !== JSON.stringify(feature.steps)
return (
-
-
e.stopPropagation()}
- >
- {/* Header */}
-
-
- Edit Feature
-
-
-
-
-
+
+
+
)
}
diff --git a/ui/src/components/ExpandProjectChat.tsx b/ui/src/components/ExpandProjectChat.tsx
index 372d629..52926cb 100644
--- a/ui/src/components/ExpandProjectChat.tsx
+++ b/ui/src/components/ExpandProjectChat.tsx
@@ -11,6 +11,10 @@ import { useExpandChat } from '../hooks/useExpandChat'
import { ChatMessage } from './ChatMessage'
import { TypingIndicator } from './TypingIndicator'
import type { ImageAttachment } from '../lib/types'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Card, CardContent } from '@/components/ui/card'
+import { Alert, AlertDescription } from '@/components/ui/alert'
// Image upload validation constants
const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5 MB
@@ -152,28 +156,28 @@ export function ExpandProjectChat({
switch (connectionStatus) {
case 'connected':
return (
-
+
Connected
)
case 'connecting':
return (
-
+
Connecting...
)
case 'error':
return (
-
+
Error
)
default:
return (
-
+
Disconnected
@@ -182,16 +186,16 @@ export function ExpandProjectChat({
}
return (
-
+
{/* Header */}
-
+
-
+
Expand Project: {projectName}
{featuresCreated > 0 && (
-
+
{featuresCreated} added
@@ -200,57 +204,63 @@ export function ExpandProjectChat({
{isComplete && (
-
+
Complete
)}
-
-
+
{/* Error banner */}
{error && (
-
+
- {error}
- {error}
+ setError(null)}
- className="p-1 hover:opacity-70 transition-opacity rounded"
+ variant="ghost"
+ size="icon"
+ className="h-6 w-6"
>
-
-
+
+
)}
{/* Messages area */}
{messages.length === 0 && !isLoading && (
-
-
- Starting Project Expansion
-
-
- Connecting to Claude to help you add new features to your project...
-
- {connectionStatus === 'error' && (
-
-
- Retry Connection
-
- )}
-
+
+
+
+ Starting Project Expansion
+
+
+ Connecting to Claude to help you add new features to your project...
+
+ {connectionStatus === 'error' && (
+
+
+ Retry Connection
+
+ )}
+
+
)}
@@ -268,7 +278,7 @@ export function ExpandProjectChat({
{/* Input area */}
{!isComplete && (
@@ -278,22 +288,21 @@ export function ExpandProjectChat({
{pendingAttachments.map((attachment) => (
{/* Help text */}
-
+
Press Enter to send. Drag & drop or click to attach images.
@@ -360,7 +370,7 @@ export function ExpandProjectChat({
{/* Completion footer */}
{isComplete && (
-
+
@@ -368,12 +378,12 @@ export function ExpandProjectChat({
Added {featuresCreated} new feature{featuresCreated !== 1 ? 's' : ''}!
-
onComplete(featuresCreated)}
- className="neo-btn bg-neo-card"
+ variant="secondary"
>
Close
-
+
)}
diff --git a/ui/src/components/ExpandProjectModal.tsx b/ui/src/components/ExpandProjectModal.tsx
index af0d196..79872b0 100644
--- a/ui/src/components/ExpandProjectModal.tsx
+++ b/ui/src/components/ExpandProjectModal.tsx
@@ -30,7 +30,7 @@ export function ExpandProjectModal({
}
return (
-
+
void
isInProgress?: boolean
allFeatures?: Feature[]
- activeAgent?: ActiveAgent // Agent working on this feature
+ activeAgent?: ActiveAgent
}
-// Generate consistent color for category using CSS variable references
-// These map to the --color-neo-category-* variables defined in globals.css
+// Generate consistent color for category
function getCategoryColor(category: string): string {
const colors = [
- 'var(--color-neo-category-pink)',
- 'var(--color-neo-category-cyan)',
- 'var(--color-neo-category-green)',
- 'var(--color-neo-category-yellow)',
- 'var(--color-neo-category-orange)',
- 'var(--color-neo-category-purple)',
- 'var(--color-neo-category-blue)',
+ 'bg-pink-500',
+ 'bg-cyan-500',
+ 'bg-green-500',
+ 'bg-yellow-500',
+ 'bg-orange-500',
+ 'bg-purple-500',
+ 'bg-blue-500',
]
let hash = 0
@@ -38,86 +39,85 @@ export function FeatureCard({ feature, onClick, isInProgress, allFeatures = [],
const hasActiveAgent = !!activeAgent
return (
-
- {/* Header */}
-
-
-
- {feature.category}
-
-
-
-
- #{feature.priority}
-
-
-
- {/* Name */}
-
- {feature.name}
-
-
- {/* Description */}
-
- {feature.description}
-
-
- {/* Agent working on this feature */}
- {activeAgent && (
-
-
-
-
- {activeAgent.agentName} is working on this!
-
- {activeAgent.thought && (
-
-
-
- {activeAgent.thought}
-
-
- )}
+
+ {/* Header */}
+
+
+
+ {feature.category}
+
+
+
+ #{feature.priority}
+
- )}
- {/* Status */}
-
- {isInProgress ? (
- <>
-
-
Processing...
- >
- ) : feature.passes ? (
- <>
-
-
Complete
- >
- ) : isBlocked ? (
- <>
-
-
Blocked
- >
- ) : (
- <>
-
-
Pending
- >
+ {/* Name */}
+
+ {feature.name}
+
+
+ {/* Description */}
+
+ {feature.description}
+
+
+ {/* Agent working on this feature */}
+ {activeAgent && (
+
+
+
+
+ {activeAgent.agentName} is working on this!
+
+ {activeAgent.thought && (
+
+
+
+ {activeAgent.thought}
+
+
+ )}
+
+
)}
-
-
+
+ {/* Status */}
+
+ {isInProgress ? (
+ <>
+
+ Processing...
+ >
+ ) : feature.passes ? (
+ <>
+
+ Complete
+ >
+ ) : isBlocked ? (
+ <>
+
+ Blocked
+ >
+ ) : (
+ <>
+
+ Pending
+ >
+ )}
+
+
+
)
}
diff --git a/ui/src/components/FeatureModal.tsx b/ui/src/components/FeatureModal.tsx
index 1853039..25f396f 100644
--- a/ui/src/components/FeatureModal.tsx
+++ b/ui/src/components/FeatureModal.tsx
@@ -3,17 +3,28 @@ import { X, CheckCircle2, Circle, SkipForward, Trash2, Loader2, AlertCircle, Pen
import { useSkipFeature, useDeleteFeature, useFeatures } from '../hooks/useProjects'
import { EditFeatureForm } from './EditFeatureForm'
import type { Feature } from '../lib/types'
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+} from '@/components/ui/dialog'
+import { Button } from '@/components/ui/button'
+import { Badge } from '@/components/ui/badge'
+import { Alert, AlertDescription } from '@/components/ui/alert'
+import { Separator } from '@/components/ui/separator'
-// Generate consistent color for category (matches FeatureCard pattern)
+// Generate consistent color for category
function getCategoryColor(category: string): string {
const colors = [
- '#ff006e', // pink (accent)
- '#00b4d8', // cyan (progress)
- '#70e000', // green (done)
- '#ffd60a', // yellow (pending)
- '#ff5400', // orange (danger)
- '#8338ec', // purple
- '#3a86ff', // blue
+ 'bg-pink-500',
+ 'bg-cyan-500',
+ 'bg-green-500',
+ 'bg-yellow-500',
+ 'bg-orange-500',
+ 'bg-purple-500',
+ 'bg-blue-500',
]
let hash = 0
@@ -90,109 +101,91 @@ export function FeatureModal({ feature, projectName, onClose }: FeatureModalProp
}
return (
-
-
e.stopPropagation()}
- >
+