diff --git a/.husky/commit-msg b/.husky/commit-msg
new file mode 100644
index 00000000..0a4b97de
--- /dev/null
+++ b/.husky/commit-msg
@@ -0,0 +1 @@
+npx --no -- commitlint --edit $1
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 00000000..2312dc58
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+npx lint-staged
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 00000000..b29b5ae8
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1,3 @@
+export default {
+ extends: ["@commitlint/config-conventional"],
+};
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 00000000..5645f2e8
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,102 @@
+import js from "@eslint/js";
+import tseslint from "typescript-eslint";
+import react from "eslint-plugin-react";
+import reactHooks from "eslint-plugin-react-hooks";
+import reactRefresh from "eslint-plugin-react-refresh";
+import importX from "eslint-plugin-import-x";
+import tailwindcss from "eslint-plugin-tailwindcss";
+import unusedImports from "eslint-plugin-unused-imports";
+import globals from "globals";
+
+export default tseslint.config(
+ {
+ ignores: ["dist/**", "node_modules/**", "public/**"],
+ },
+ {
+ files: ["src/**/*.{ts,tsx,js,jsx}"],
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
+ plugins: {
+ react,
+ "react-hooks": reactHooks, // for following React rules such as dependencies in hooks, keys in lists, etc.
+ "react-refresh": reactRefresh, // for Vite HMR compatibility
+ "import-x": importX, // for import order/sorting. It also detercts circular dependencies and duplicate imports.
+ tailwindcss, // for detecting invalid Tailwind classnames and enforcing classname order
+ "unused-imports": unusedImports, // for detecting unused imports
+ },
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ },
+ parserOptions: {
+ ecmaFeatures: { jsx: true },
+ },
+ },
+ settings: {
+ react: { version: "detect" },
+ },
+ rules: {
+ // --- Unused imports/vars ---
+ "unused-imports/no-unused-imports": "warn",
+ "unused-imports/no-unused-vars": [
+ "warn",
+ {
+ vars: "all",
+ varsIgnorePattern: "^_",
+ args: "after-used",
+ argsIgnorePattern: "^_",
+ },
+ ],
+ "no-unused-vars": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+
+ // --- React ---
+ "react/jsx-key": "warn",
+ "react/jsx-no-duplicate-props": "error",
+ "react/jsx-no-undef": "error",
+ "react/no-children-prop": "warn",
+ "react/no-danger-with-children": "error",
+ "react/no-direct-mutation-state": "error",
+ "react/no-unknown-property": "warn",
+ "react/react-in-jsx-scope": "off",
+
+ // --- React Hooks ---
+ "react-hooks/rules-of-hooks": "error",
+ "react-hooks/exhaustive-deps": "warn",
+
+ // --- React Refresh (Vite HMR) ---
+ "react-refresh/only-export-components": [
+ "warn",
+ { allowConstantExport: true },
+ ],
+
+ // --- Import ordering & hygiene ---
+ "import-x/no-duplicates": "warn",
+ "import-x/order": [
+ "warn",
+ {
+ groups: [
+ "builtin",
+ "external",
+ "internal",
+ "parent",
+ "sibling",
+ "index",
+ ],
+ "newlines-between": "never",
+ },
+ ],
+
+ // --- Tailwind CSS ---
+ "tailwindcss/classnames-order": "warn",
+ "tailwindcss/no-contradicting-classname": "warn",
+ "tailwindcss/no-unnecessary-arbitrary-value": "warn",
+
+ // --- Disabled base rules ---
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-require-imports": "off",
+ "no-case-declarations": "off",
+ "no-control-regex": "off",
+ "no-useless-escape": "off",
+ },
+ }
+);
diff --git a/package-lock.json b/package-lock.json
index 48a89efc..e2df5bd7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -72,6 +72,9 @@
"cloudcli": "server/cli.js"
},
"devDependencies": {
+ "@commitlint/cli": "^20.4.3",
+ "@commitlint/config-conventional": "^20.4.3",
+ "@eslint/js": "^9.39.3",
"@release-it/conventional-changelog": "^10.0.5",
"@types/node": "^22.19.7",
"@types/react": "^18.2.43",
@@ -80,12 +83,23 @@
"auto-changelog": "^2.5.0",
"autoprefixer": "^10.4.16",
"concurrently": "^8.2.2",
+ "eslint": "^9.39.3",
+ "eslint-plugin-import-x": "^4.16.1",
+ "eslint-plugin-react": "^7.37.5",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.5.2",
+ "eslint-plugin-tailwindcss": "^3.18.2",
+ "eslint-plugin-unused-imports": "^4.4.1",
+ "globals": "^17.4.0",
+ "husky": "^9.1.7",
+ "lint-staged": "^16.3.2",
"node-gyp": "^10.0.0",
"postcss": "^8.4.32",
"release-it": "^19.0.5",
"sharp": "^0.34.2",
"tailwindcss": "^3.4.0",
"typescript": "^5.9.3",
+ "typescript-eslint": "^8.56.1",
"vite": "^7.0.4"
}
},
@@ -618,6 +632,299 @@
"w3c-keyname": "^2.2.4"
}
},
+ "node_modules/@commitlint/cli": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.4.3.tgz",
+ "integrity": "sha512-Z37EMoDT7+Upg500vlr/vZrgRsb6Xc5JAA3Tv7BYbobnN/ZpqUeZnSLggBg2+1O+NptRDtyujr2DD1CPV2qwhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/format": "^20.4.3",
+ "@commitlint/lint": "^20.4.3",
+ "@commitlint/load": "^20.4.3",
+ "@commitlint/read": "^20.4.3",
+ "@commitlint/types": "^20.4.3",
+ "tinyexec": "^1.0.0",
+ "yargs": "^17.0.0"
+ },
+ "bin": {
+ "commitlint": "cli.js"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-conventional": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.4.3.tgz",
+ "integrity": "sha512-9RtLySbYQAs8yEqWEqhSZo9nYhbm57jx7qHXtgRmv/nmeQIjjMcwf6Dl+y5UZcGWgWx435TAYBURONaJIuCjWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.4.3",
+ "conventional-changelog-conventionalcommits": "^9.2.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-validator": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.4.3.tgz",
+ "integrity": "sha512-jCZpZFkcSL3ZEdL5zgUzFRdytv3xPo8iukTe9VA+QGus/BGhpp1xXSVu2B006GLLb2gYUAEGEqv64kTlpZNgmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.4.3",
+ "ajv": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-validator/node_modules/ajv": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
+ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@commitlint/ensure": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.4.3.tgz",
+ "integrity": "sha512-WcXGKBNn0wBKpX8VlXgxqedyrLxedIlLBCMvdamLnJFEbUGJ9JZmBVx4vhLV3ZyA8uONGOb+CzW0Y9HDbQ+ONQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.4.3",
+ "lodash.camelcase": "^4.3.0",
+ "lodash.kebabcase": "^4.1.1",
+ "lodash.snakecase": "^4.1.1",
+ "lodash.startcase": "^4.4.0",
+ "lodash.upperfirst": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/execute-rule": {
+ "version": "20.0.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz",
+ "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/format": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.4.3.tgz",
+ "integrity": "sha512-UDJVErjLbNghop6j111rsHJYGw6MjCKAi95K0GT2yf4eeiDHy3JDRLWYWEjIaFgO+r+dQSkuqgJ1CdMTtrvHsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.4.3",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/is-ignored": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.4.3.tgz",
+ "integrity": "sha512-W5VQKZ7fdJ1X3Tko+h87YZaqRMGN1KvQKXyCM8xFdxzMIf1KCZgN4uLz3osLB1zsFcVS4ZswHY64LI26/9ACag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.4.3",
+ "semver": "^7.6.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/is-ignored/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@commitlint/lint": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.4.3.tgz",
+ "integrity": "sha512-CYOXL23e+nRKij81+d0+dymtIi7Owl9QzvblJYbEfInON/4MaETNSLFDI74LDu+YJ0ML5HZyw9Vhp9QpckwQ0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/is-ignored": "^20.4.3",
+ "@commitlint/parse": "^20.4.3",
+ "@commitlint/rules": "^20.4.3",
+ "@commitlint/types": "^20.4.3"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/load": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.4.3.tgz",
+ "integrity": "sha512-3cdJOUVP+VcgHa7bhJoWS+Z8mBNXB5aLWMBu7Q7uX8PSeWDzdbrBlR33J1MGGf7r1PZDp+mPPiFktk031PgdRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/config-validator": "^20.4.3",
+ "@commitlint/execute-rule": "^20.0.0",
+ "@commitlint/resolve-extends": "^20.4.3",
+ "@commitlint/types": "^20.4.3",
+ "cosmiconfig": "^9.0.1",
+ "cosmiconfig-typescript-loader": "^6.1.0",
+ "is-plain-obj": "^4.1.0",
+ "lodash.mergewith": "^4.6.2",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/message": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.3.tgz",
+ "integrity": "sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/parse": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.4.3.tgz",
+ "integrity": "sha512-hzC3JCo3zs3VkQ833KnGVuWjWIzR72BWZWjQM7tY/7dfKreKAm7fEsy71tIFCRtxf2RtMP2d3RLF1U9yhFSccA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^20.4.3",
+ "conventional-changelog-angular": "^8.2.0",
+ "conventional-commits-parser": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/read": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.4.3.tgz",
+ "integrity": "sha512-j42OWv3L31WfnP8WquVjHZRt03w50Y/gEE8FAyih7GQTrIv2+pZ6VZ6pWLD/ml/3PO+RV2SPtRtTp/MvlTb8rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/top-level": "^20.4.3",
+ "@commitlint/types": "^20.4.3",
+ "git-raw-commits": "^4.0.0",
+ "minimist": "^1.2.8",
+ "tinyexec": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/resolve-extends": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.4.3.tgz",
+ "integrity": "sha512-QucxcOy+00FhS9s4Uy0OyS5HeUV+hbC6OLqkTSIm6fwMdKva+OEavaCDuLtgd9akZZlsUo//XzSmPP3sLKBPog==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/config-validator": "^20.4.3",
+ "@commitlint/types": "^20.4.3",
+ "global-directory": "^4.0.1",
+ "import-meta-resolve": "^4.0.0",
+ "lodash.mergewith": "^4.6.2",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/rules": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.4.3.tgz",
+ "integrity": "sha512-Yuosd7Grn5qiT7FovngXLyRXTMUbj9PYiSkvUgWK1B5a7+ZvrbWDS7epeUapYNYatCy/KTpPFPbgLUdE+MUrBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/ensure": "^20.4.3",
+ "@commitlint/message": "^20.4.3",
+ "@commitlint/to-lines": "^20.0.0",
+ "@commitlint/types": "^20.4.3"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/to-lines": {
+ "version": "20.0.0",
+ "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz",
+ "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/top-level": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.3.tgz",
+ "integrity": "sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/types": {
+ "version": "20.4.3",
+ "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.4.3.tgz",
+ "integrity": "sha512-51OWa1Gi6ODOasPmfJPq6js4pZoomima4XLZZCrkldaH2V5Nb3bVhNXPeT6XV0gubbainSpTw4zi68NqAeCNCg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "conventional-commits-parser": "^6.3.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
"node_modules/@conventional-changelog/git-client": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.5.1.tgz",
@@ -658,6 +965,18 @@
"node": ">=10"
}
},
+ "node_modules/@emnapi/core": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
+ "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.1.0",
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@emnapi/runtime": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
@@ -669,6 +988,17 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
+ "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
@@ -1111,6 +1441,244 @@
"node": ">=18"
}
},
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.4.tgz",
+ "integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.14.0",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.1",
+ "minimatch": "^3.1.3",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.3",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz",
+ "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
"node_modules/@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
@@ -1118,6 +1686,58 @@
"license": "MIT",
"optional": true
},
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
"node_modules/@iarna/toml": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz",
@@ -2124,6 +2744,19 @@
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
"license": "MIT"
},
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
+ "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@tybys/wasm-util": "^0.10.0"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -3000,14 +3633,11 @@
}
},
"node_modules/@simple-libs/stream-utils": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.1.0.tgz",
- "integrity": "sha512-6rsHTjodIn/t90lv5snQjRPVtOosM7Vp0AKdrObymq45ojlgVwnpAqdc+0OBBrpEiy31zZ6/TKeIVqV1HwvnuQ==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz",
+ "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "@types/node": "^22.0.0"
- },
"engines": {
"node": ">=18"
},
@@ -3047,6 +3677,17 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -3125,6 +3766,13 @@
"@types/unist": "*"
}
},
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/katex": {
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz",
@@ -3202,6 +3850,301 @@
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
"license": "MIT"
},
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz",
+ "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.56.1",
+ "@typescript-eslint/type-utils": "8.56.1",
+ "@typescript-eslint/utils": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.56.1",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz",
+ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.56.1",
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz",
+ "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.56.1",
+ "@typescript-eslint/types": "^8.56.1",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz",
+ "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz",
+ "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz",
+ "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1",
+ "@typescript-eslint/utils": "8.56.1",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz",
+ "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz",
+ "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.56.1",
+ "@typescript-eslint/tsconfig-utils": "8.56.1",
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz",
+ "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.56.1",
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz",
+ "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.56.1",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/@uiw/codemirror-extensions-basic-setup": {
"version": "4.24.1",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.24.1.tgz",
@@ -3261,6 +4204,275 @@
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"license": "ISC"
},
+ "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz",
+ "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-android-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz",
+ "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz",
+ "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz",
+ "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz",
+ "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz",
+ "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz",
+ "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz",
+ "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz",
+ "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz",
+ "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz",
+ "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz",
+ "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz",
+ "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz",
+ "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz",
+ "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz",
+ "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.2.11"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz",
+ "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz",
+ "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz",
+ "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@vitejs/plugin-react": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
@@ -3371,6 +4583,29 @@
"node": ">= 0.6"
}
},
+ "node_modules/acorn": {
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
@@ -3408,6 +4643,39 @@
"node": ">=8"
}
},
+ "node_modules/ajv": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
+ "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/ansi-regex": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
@@ -3497,6 +4765,23 @@
"sprintf-js": "~1.0.2"
}
},
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -3510,6 +4795,127 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/ast-types": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
@@ -3523,6 +4929,16 @@
"node": ">=4"
}
},
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/async-retry": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
@@ -3634,6 +5050,22 @@
"postcss": "^8.1.0"
}
},
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/bail": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
@@ -3994,6 +5426,25 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -4023,6 +5474,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
@@ -4238,6 +5699,40 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/cli-truncate": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz",
+ "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^8.0.0",
+ "string-width": "^8.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/string-width": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz",
+ "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.5.0",
+ "strip-ansi": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/cli-width": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
@@ -4403,6 +5898,13 @@
"color-support": "bin.js"
}
},
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/comma-separated-tokens": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
@@ -4422,6 +5924,16 @@
"node": ">= 6"
}
},
+ "node_modules/comment-parser": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.5.tgz",
+ "integrity": "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
"node_modules/compare-func": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
@@ -4437,8 +5949,8 @@
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "license": "MIT",
- "optional": true
+ "devOptional": true,
+ "license": "MIT"
},
"node_modules/concat-stream": {
"version": "2.0.0",
@@ -4552,9 +6064,9 @@
}
},
"node_modules/conventional-changelog-angular": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.1.0.tgz",
- "integrity": "sha512-GGf2Nipn1RUCAktxuVauVr1e3r8QrLP/B0lEUsFktmGqc3ddbQkhoJZHJctVU829U1c6mTSWftrVOCHaL85Q3w==",
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.0.tgz",
+ "integrity": "sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -4565,9 +6077,9 @@
}
},
"node_modules/conventional-changelog-conventionalcommits": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.1.0.tgz",
- "integrity": "sha512-MnbEysR8wWa8dAEvbj5xcBgJKQlX/m0lhS8DsyAAWDHdfs2faDJxTgzRYlRYpXSe7UiKrIIlB4TrBKU9q9DgkA==",
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.3.0.tgz",
+ "integrity": "sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -4630,12 +6142,13 @@
}
},
"node_modules/conventional-commits-parser": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.2.1.tgz",
- "integrity": "sha512-20pyHgnO40rvfI0NGF/xiEoFMkXDtkF8FwHvk5BokoFoCuTQRI8vrNCNFWUOfuolKJMm1tPCHc8GgYEtr1XRNA==",
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.3.0.tgz",
+ "integrity": "sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg==",
"dev": true,
"license": "MIT",
"dependencies": {
+ "@simple-libs/stream-utils": "^1.2.0",
"meow": "^13.0.0"
},
"bin": {
@@ -4706,6 +6219,81 @@
"node": ">= 0.10"
}
},
+ "node_modules/cosmiconfig": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz",
+ "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cosmiconfig-typescript-loader": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz",
+ "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jiti": "^2.6.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ },
+ "peerDependencies": {
+ "@types/node": "*",
+ "cosmiconfig": ">=9",
+ "typescript": ">=5"
+ }
+ },
+ "node_modules/cosmiconfig-typescript-loader/node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/cosmiconfig/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/cosmiconfig/node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/crelt": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
@@ -4744,6 +6332,19 @@
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
+ "node_modules/dargs": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz",
+ "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/data-uri-to-buffer": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
@@ -4754,6 +6355,60 @@
"node": ">= 14"
}
},
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/date-fns": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
@@ -4825,6 +6480,13 @@
"node": ">=4.0.0"
}
},
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/default-browser": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz",
@@ -4855,6 +6517,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/define-lazy-prop": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
@@ -4868,6 +6548,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/defu": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
@@ -4966,6 +6664,19 @@
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"license": "MIT"
},
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/dot-prop": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
@@ -5103,6 +6814,19 @@
"node": ">=6"
}
},
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/err-code": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
@@ -5110,6 +6834,92 @@
"devOptional": true,
"license": "MIT"
},
+ "node_modules/error-ex": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/error-ex/node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
+ "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -5128,6 +6938,34 @@
"node": ">= 0.4"
}
},
+ "node_modules/es-iterator-helpers": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz",
+ "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.1",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.1.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.3.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.5",
+ "safe-array-concat": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
@@ -5140,6 +6978,53 @@
"node": ">= 0.4"
}
},
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/esbuild": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
@@ -5232,6 +7117,370 @@
"source-map": "~0.6.1"
}
},
+ "node_modules/eslint": {
+ "version": "9.39.3",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz",
+ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.3",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-import-context": {
+ "version": "0.1.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz",
+ "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-tsconfig": "^4.10.1",
+ "stable-hash-x": "^0.2.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-import-context"
+ },
+ "peerDependencies": {
+ "unrs-resolver": "^1.0.0"
+ },
+ "peerDependenciesMeta": {
+ "unrs-resolver": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-import-x": {
+ "version": "4.16.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.1.tgz",
+ "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "^8.35.0",
+ "comment-parser": "^1.4.1",
+ "debug": "^4.4.1",
+ "eslint-import-context": "^0.1.9",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.3 || ^10.0.1",
+ "semver": "^7.7.2",
+ "stable-hash-x": "^0.2.0",
+ "unrs-resolver": "^1.9.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-import-x"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/utils": "^8.0.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "eslint-import-resolver-node": "*"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/utils": {
+ "optional": true
+ },
+ "eslint-import-resolver-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-import-x/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.9",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz",
+ "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": "^9 || ^10"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.6",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz",
+ "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
+ "node-exports-info": "^1.6.0",
+ "object-keys": "^1.1.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-plugin-tailwindcss": {
+ "version": "3.18.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.18.2.tgz",
+ "integrity": "sha512-QbkMLDC/OkkjFQ1iz/5jkMdHfiMu/uwujUHLAJK5iwNHD8RTxVTlsUezE0toTZ6VhybNBsk+gYGPDq2agfeRNA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-glob": "^3.2.5",
+ "postcss": "^8.4.4"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "peerDependencies": {
+ "tailwindcss": "^3.4.0"
+ }
+ },
+ "node_modules/eslint-plugin-unused-imports": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz",
+ "integrity": "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0",
+ "eslint": "^10.0.0 || ^9.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/eslint-plugin": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
@@ -5245,6 +7494,32 @@
"node": ">=4"
}
},
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
"node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
@@ -5297,6 +7572,13 @@
"node": ">= 0.6"
}
},
+ "node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/execa": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
@@ -5455,6 +7737,13 @@
],
"license": "MIT"
},
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -5483,6 +7772,37 @@
"node": ">= 6"
}
},
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
"node_modules/fastq": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
@@ -5515,6 +7835,19 @@
"walk-up-path": "^4.0.0"
}
},
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/file-selector": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz",
@@ -5578,6 +7911,60 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz",
+ "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/foreground-child": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
@@ -5700,6 +8087,37 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/fuse.js": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
@@ -5782,6 +8200,16 @@
"node": ">=8"
}
},
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -5865,6 +8293,37 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
+ "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
"node_modules/get-uri": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
@@ -5898,6 +8357,38 @@
"giget": "dist/cli.mjs"
}
},
+ "node_modules/git-raw-commits": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz",
+ "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==",
+ "deprecated": "This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dargs": "^8.0.0",
+ "meow": "^12.0.1",
+ "split2": "^4.0.0"
+ },
+ "bin": {
+ "git-raw-commits": "cli.mjs"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/git-raw-commits/node_modules/meow": {
+ "version": "12.1.1",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz",
+ "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/git-up": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/git-up/-/git-up-8.1.1.tgz",
@@ -5957,6 +8448,62 @@
"node": ">=10.13.0"
}
},
+ "node_modules/global-directory": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz",
+ "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "4.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/global-directory/node_modules/ini": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
+ "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "17.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz",
+ "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -6013,6 +8560,19 @@
"uglify-js": "^3.1.4"
}
},
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -6023,6 +8583,35 @@
"node": ">=8"
}
},
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -6035,6 +8624,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
@@ -6266,6 +8871,23 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
"node_modules/highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
@@ -6401,6 +9023,22 @@
"ms": "^2.0.0"
}
},
+ "node_modules/husky": {
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
+ "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "husky": "bin.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
"node_modules/i18next": {
"version": "25.7.4",
"resolved": "https://registry.npmmirror.com/i18next/-/i18next-25.7.4.tgz",
@@ -6473,6 +9111,16 @@
],
"license": "BSD-3-Clause"
},
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
@@ -6492,6 +9140,33 @@
"node": ">=8"
}
},
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/import-from": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz",
@@ -6505,6 +9180,17 @@
"node": ">=8"
}
},
+ "node_modules/import-meta-resolve": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
+ "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -6589,6 +9275,21 @@
}
}
},
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/ip-address": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
@@ -6632,6 +9333,24 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
@@ -6639,6 +9358,42 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -6651,6 +9406,36 @@
"node": ">=8"
}
},
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
@@ -6666,6 +9451,41 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-decimal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
@@ -6710,6 +9530,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@@ -6719,6 +9555,26 @@
"node": ">=8"
}
},
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -6780,6 +9636,32 @@
"devOptional": true,
"license": "MIT"
},
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -6789,6 +9671,23 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
@@ -6811,6 +9710,54 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-ssh": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.1.tgz",
@@ -6834,6 +9781,57 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-unicode-supported": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
@@ -6847,6 +9845,52 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-wsl": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz",
@@ -6892,6 +9936,24 @@
"node": "^18.17 || >=20.6.1"
}
},
+ "node_modules/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
@@ -6954,6 +10016,34 @@
"node": ">=6"
}
},
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -7001,6 +10091,22 @@
"node": ">=10"
}
},
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -7089,6 +10195,16 @@
"node": ">= 12"
}
},
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
"node_modules/kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -7098,6 +10214,20 @@
"node": ">=0.10.0"
}
},
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
@@ -7125,6 +10255,130 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"license": "MIT"
},
+ "node_modules/lint-staged": {
+ "version": "16.3.2",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.3.2.tgz",
+ "integrity": "sha512-xKqhC2AeXLwiAHXguxBjuChoTTWFC6Pees0SHPwOpwlvI3BH7ZADFPddAdN3pgo3aiKgPUx/bxE78JfUnxQnlg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^14.0.3",
+ "listr2": "^9.0.5",
+ "micromatch": "^4.0.8",
+ "string-argv": "^0.3.2",
+ "tinyexec": "^1.0.2",
+ "yaml": "^2.8.2"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=20.17"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ }
+ },
+ "node_modules/lint-staged/node_modules/commander": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz",
+ "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^5.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/listr2/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/listr2/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/listr2/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -7132,6 +10386,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.capitalize": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz",
@@ -7188,18 +10449,46 @@
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT"
},
+ "node_modules/lodash.kebabcase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
+ "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"license": "MIT"
},
+ "node_modules/lodash.mergewith": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
"license": "MIT"
},
+ "node_modules/lodash.snakecase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+ "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.startcase": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz",
+ "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.uniqby": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
@@ -7207,6 +10496,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/lodash.upperfirst": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
+ "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/log-symbols": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz",
@@ -7224,6 +10520,115 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
+ "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/longest-streak": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
@@ -8627,6 +12032,29 @@
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
"license": "MIT"
},
+ "node_modules/napi-postinstall": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+ "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -8702,6 +12130,25 @@
"node": "^18 || ^20 || >= 21"
}
},
+ "node_modules/node-exports-info": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz",
+ "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array.prototype.flatmap": "^1.3.3",
+ "es-errors": "^1.3.0",
+ "object.entries": "^1.1.9",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -8986,6 +12433,91 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/ohash": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
@@ -9049,6 +12581,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/ora": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/ora/-/ora-9.0.0.tgz",
@@ -9120,6 +12670,56 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/p-map": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
@@ -9182,6 +12782,19 @@
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"license": "(MIT AND Zlib)"
},
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/parse-entities": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
@@ -9220,6 +12833,25 @@
"node": ">= 0.10"
}
},
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/parse-path": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.1.0.tgz",
@@ -9265,6 +12897,16 @@
"node": ">= 0.8"
}
},
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -9380,6 +13022,16 @@
"pathe": "^2.0.3"
}
},
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
@@ -9562,6 +13214,16 @@
"node": ">=10"
}
},
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/prismjs": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
@@ -9696,6 +13358,16 @@
"once": "^1.3.1"
}
},
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
@@ -9990,6 +13662,29 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/refractor": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz",
@@ -10181,6 +13876,27 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/rehype-katex": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz",
@@ -10368,6 +14084,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -10398,6 +14124,16 @@
"node": ">=8"
}
},
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
"node_modules/restore-cursor": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
@@ -10435,6 +14171,13 @@
"node": ">=0.10.0"
}
},
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -10594,6 +14337,33 @@
"tslib": "^2.1.0"
}
},
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-array-concat/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -10614,6 +14384,48 @@
],
"license": "MIT"
},
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-push-apply/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -10722,6 +14534,55 @@
"license": "ISC",
"optional": true
},
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@@ -11183,6 +15044,52 @@
"is-arrayish": "^0.3.1"
}
},
+ "node_modules/slice-ansi": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz",
+ "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.3",
+ "is-fullwidth-code-point": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
@@ -11295,6 +15202,16 @@
"dev": true,
"license": "CC0-1.0"
},
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -11703,6 +15620,16 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
+ "node_modules/stable-hash-x": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz",
+ "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -11725,6 +15652,20 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@@ -11742,6 +15683,16 @@
"safe-buffer": "~5.2.0"
}
},
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.19"
+ }
+ },
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@@ -11801,6 +15752,104 @@
"node": ">=8"
}
},
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/stringify-entities": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
@@ -12168,11 +16217,14 @@
}
},
"node_modules/tinyexec": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz",
- "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
+ "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/tinyglobby": {
"version": "0.2.15",
@@ -12279,6 +16331,19 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/ts-api-utils": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
+ "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
@@ -12303,6 +16368,19 @@
"node": "*"
}
},
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/type-fest": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
@@ -12350,6 +16428,84 @@
"node": ">= 0.6"
}
},
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -12370,6 +16526,30 @@
"node": ">=14.17"
}
},
+ "node_modules/typescript-eslint": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.1.tgz",
+ "integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.56.1",
+ "@typescript-eslint/parser": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1",
+ "@typescript-eslint/utils": "8.56.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
"node_modules/uglify-js": {
"version": "3.19.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
@@ -12384,6 +16564,25 @@
"node": ">=0.8.0"
}
},
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/undici": {
"version": "6.21.3",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
@@ -12557,6 +16756,41 @@
"node": ">= 0.8"
}
},
+ "node_modules/unrs-resolver": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
+ "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "napi-postinstall": "^0.3.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unrs-resolver"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-android-arm-eabi": "1.11.1",
+ "@unrs/resolver-binding-android-arm64": "1.11.1",
+ "@unrs/resolver-binding-darwin-arm64": "1.11.1",
+ "@unrs/resolver-binding-darwin-x64": "1.11.1",
+ "@unrs/resolver-binding-freebsd-x64": "1.11.1",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.11.1",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-x64-musl": "1.11.1",
+ "@unrs/resolver-binding-wasm32-wasi": "1.11.1",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -12588,6 +16822,16 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
"node_modules/url-join": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz",
@@ -12856,6 +17100,102 @@
"node": ">= 8"
}
},
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.20",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
+ "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
@@ -12934,6 +17274,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
@@ -13099,15 +17449,18 @@
"license": "ISC"
},
"node_modules/yaml": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
- "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
+ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/yargs": {
@@ -13184,6 +17537,19 @@
"node": ">=8"
}
},
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/yoctocolors": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
@@ -13215,11 +17581,23 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
- "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ },
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
diff --git a/package.json b/package.json
index cb4398f5..4b2c9980 100644
--- a/package.json
+++ b/package.json
@@ -30,10 +30,13 @@
"build": "vite build",
"preview": "vite preview",
"typecheck": "tsc --noEmit -p tsconfig.json",
+ "lint": "eslint src/",
+ "lint:fix": "eslint src/ --fix",
"start": "npm run build && npm run server",
"release": "./release.sh",
"prepublishOnly": "npm run build",
- "postinstall": "node scripts/fix-node-pty.js"
+ "postinstall": "node scripts/fix-node-pty.js",
+ "prepare": "husky"
},
"keywords": [
"claude code",
@@ -103,6 +106,9 @@
"ws": "^8.14.2"
},
"devDependencies": {
+ "@commitlint/cli": "^20.4.3",
+ "@commitlint/config-conventional": "^20.4.3",
+ "@eslint/js": "^9.39.3",
"@release-it/conventional-changelog": "^10.0.5",
"@types/node": "^22.19.7",
"@types/react": "^18.2.43",
@@ -111,12 +117,26 @@
"auto-changelog": "^2.5.0",
"autoprefixer": "^10.4.16",
"concurrently": "^8.2.2",
+ "eslint": "^9.39.3",
+ "eslint-plugin-import-x": "^4.16.1",
+ "eslint-plugin-react": "^7.37.5",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.5.2",
+ "eslint-plugin-tailwindcss": "^3.18.2",
+ "eslint-plugin-unused-imports": "^4.4.1",
+ "globals": "^17.4.0",
+ "husky": "^9.1.7",
+ "lint-staged": "^16.3.2",
"node-gyp": "^10.0.0",
"postcss": "^8.4.32",
"release-it": "^19.0.5",
"sharp": "^0.34.2",
"tailwindcss": "^3.4.0",
"typescript": "^5.9.3",
+ "typescript-eslint": "^8.56.1",
"vite": "^7.0.4"
+ },
+ "lint-staged": {
+ "src/**/*.{ts,tsx,js,jsx}": "eslint"
}
}
diff --git a/src/App.tsx b/src/App.tsx
index 8593236f..564ee1a3 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,11 +1,10 @@
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { I18nextProvider } from 'react-i18next';
import { ThemeProvider } from './contexts/ThemeContext';
-import { AuthProvider } from './contexts/AuthContext';
+import { AuthProvider, ProtectedRoute } from './components/auth';
import { TaskMasterProvider } from './contexts/TaskMasterContext';
import { TasksSettingsProvider } from './contexts/TasksSettingsContext';
import { WebSocketProvider } from './contexts/WebSocketContext';
-import ProtectedRoute from './components/ProtectedRoute';
import AppContent from './components/app/AppContent';
import i18n from './i18n/config.js';
diff --git a/src/components/CreateTaskModal.jsx b/src/components/CreateTaskModal.jsx
deleted file mode 100644
index 81af285a..00000000
--- a/src/components/CreateTaskModal.jsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import React from 'react';
-import { X, Sparkles } from 'lucide-react';
-
-const CreateTaskModal = ({ currentProject, onClose, onTaskCreated }) => {
-
- return (
-
-
- {/* Header */}
-
-
-
-
-
-
Create AI-Generated Task
-
-
-
-
-
-
- {/* Content */}
-
- {/* AI-First Approach */}
-
-
-
-
-
-
-
- 💡 Pro Tip: Ask Claude Code Directly!
-
-
- You can simply ask Claude Code in the chat to create tasks for you.
- The AI assistant will automatically generate detailed tasks with research-backed insights.
-
-
-
-
Example:
-
- "Please add a new task to implement user profile image uploads using Cloudinary, research the best approach."
-
-
-
-
- This runs:
- task-master add-task --prompt="Implement user profile image uploads using Cloudinary" --research
-
-
-
-
-
-
- {/* Learn More Link */}
-
-
- {/* Footer */}
-
-
- Got it, I'll ask Claude Code directly
-
-
-
-
-
- );
-};
-
-export default CreateTaskModal;
\ No newline at end of file
diff --git a/src/components/DiffViewer.jsx b/src/components/DiffViewer.jsx
deleted file mode 100644
index a6d963a3..00000000
--- a/src/components/DiffViewer.jsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import React from 'react';
-
-function DiffViewer({ diff, fileName, isMobile, wrapText }) {
- if (!diff) {
- return (
-
- No diff available
-
- );
- }
-
- const renderDiffLine = (line, index) => {
- const isAddition = line.startsWith('+') && !line.startsWith('+++');
- const isDeletion = line.startsWith('-') && !line.startsWith('---');
- const isHeader = line.startsWith('@@');
-
- return (
-
- {line}
-
- );
- };
-
- return (
-
- {diff.split('\n').map((line, index) => renderDiffLine(line, index))}
-
- );
-}
-
-export default DiffViewer;
\ No newline at end of file
diff --git a/src/components/ErrorBoundary.jsx b/src/components/ErrorBoundary.jsx
deleted file mode 100644
index b20c0288..00000000
--- a/src/components/ErrorBoundary.jsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import React, { useCallback, useState } from 'react';
-import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
-
-function ErrorFallback({ error, resetErrorBoundary, showDetails, componentStack }) {
- return (
-
-
-
-
-
- Something went wrong
-
-
-
-
An error occurred while loading the chat interface.
- {showDetails && error && (
-
- Error Details
-
- {error.toString()}
- {componentStack}
-
-
- )}
-
-
-
- Try Again
-
-
-
-
- );
-}
-
-function ErrorBoundary({ children, showDetails = false, onRetry = undefined, resetKeys = undefined }) {
- const [componentStack, setComponentStack] = useState(null);
-
- const handleError = useCallback((error, errorInfo) => {
- console.error('ErrorBoundary caught an error:', error, errorInfo);
- setComponentStack(errorInfo?.componentStack || null);
- }, []);
-
- const handleReset = useCallback(() => {
- setComponentStack(null);
- onRetry?.();
- }, [onRetry]);
-
- const renderFallback = useCallback(({ error, resetErrorBoundary }) => (
-
- ), [showDetails, componentStack]);
-
- return (
-
- {children}
-
- );
-}
-
-export default ErrorBoundary;
diff --git a/src/components/FileContextMenu.jsx b/src/components/FileContextMenu.jsx
deleted file mode 100644
index 5fee1ef9..00000000
--- a/src/components/FileContextMenu.jsx
+++ /dev/null
@@ -1,312 +0,0 @@
-import React, { useState, useEffect, useRef, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-import {
- FileText,
- FolderPlus,
- Pencil,
- Trash2,
- Copy,
- Download,
- RefreshCw
-} from 'lucide-react';
-import { cn } from '../lib/utils';
-
-/**
- * FileContextMenu Component
- * Right-click context menu for file/directory operations
- */
-const FileContextMenu = ({
- children,
- item,
- onRename,
- onDelete,
- onNewFile,
- onNewFolder,
- onRefresh,
- onCopyPath,
- onDownload,
- isLoading = false,
- className = ''
-}) => {
- const { t } = useTranslation();
- const [isOpen, setIsOpen] = useState(false);
- const [position, setPosition] = useState({ x: 0, y: 0 });
- const menuRef = useRef(null);
- const triggerRef = useRef(null);
-
- const isDirectory = item?.type === 'directory';
- const isFile = item?.type === 'file';
- const isBackground = !item; // Clicked on empty space
-
- // Handle right-click
- const handleContextMenu = useCallback((e) => {
- e.preventDefault();
- e.stopPropagation();
-
- const rect = e.currentTarget.getBoundingClientRect();
- const x = e.clientX;
- const y = e.clientY;
-
- // Adjust position if menu would go off screen
- const menuWidth = 200;
- const menuHeight = 300;
-
- let adjustedX = x;
- let adjustedY = y;
-
- if (x + menuWidth > window.innerWidth) {
- adjustedX = window.innerWidth - menuWidth - 10;
- }
- if (y + menuHeight > window.innerHeight) {
- adjustedY = window.innerHeight - menuHeight - 10;
- }
-
- setPosition({ x: adjustedX, y: adjustedY });
- setIsOpen(true);
- }, []);
-
- // Close menu
- const closeMenu = useCallback(() => {
- setIsOpen(false);
- }, []);
-
- // Close on click outside
- useEffect(() => {
- const handleClickOutside = (e) => {
- if (menuRef.current && !menuRef.current.contains(e.target)) {
- closeMenu();
- }
- };
-
- const handleEscape = (e) => {
- if (e.key === 'Escape') {
- closeMenu();
- }
- };
-
- if (isOpen) {
- document.addEventListener('mousedown', handleClickOutside);
- document.addEventListener('keydown', handleEscape);
- }
-
- return () => {
- document.removeEventListener('mousedown', handleClickOutside);
- document.removeEventListener('keydown', handleEscape);
- };
- }, [isOpen, closeMenu]);
-
- // Handle keyboard navigation
- useEffect(() => {
- if (!isOpen) return;
-
- const handleKeyDown = (e) => {
- const menuItems = menuRef.current?.querySelectorAll('[role="menuitem"]');
- if (!menuItems || menuItems.length === 0) return;
-
- const currentIndex = Array.from(menuItems).findIndex(
- (item) => item === document.activeElement
- );
-
- switch (e.key) {
- case 'ArrowDown':
- e.preventDefault();
- const nextIndex = currentIndex < menuItems.length - 1 ? currentIndex + 1 : 0;
- menuItems[nextIndex]?.focus();
- break;
- case 'ArrowUp':
- e.preventDefault();
- const prevIndex = currentIndex > 0 ? currentIndex - 1 : menuItems.length - 1;
- menuItems[prevIndex]?.focus();
- break;
- case 'Enter':
- case ' ':
- if (document.activeElement?.hasAttribute('role', 'menuitem')) {
- e.preventDefault();
- document.activeElement.click();
- }
- break;
- }
- };
-
- document.addEventListener('keydown', handleKeyDown);
- return () => document.removeEventListener('keydown', handleKeyDown);
- }, [isOpen]);
-
- // Handle action click
- const handleAction = (action, ...args) => {
- closeMenu();
- action?.(...args);
- };
-
- // Menu item component
- const MenuItem = ({ icon: Icon, label, onClick, danger = false, disabled = false, shortcut }) => (
- handleAction(onClick)}
- className={cn(
- 'w-full flex items-center gap-3 px-3 py-2 text-sm text-left rounded-md transition-colors',
- 'focus:outline-none focus:bg-accent',
- disabled
- ? 'opacity-50 cursor-not-allowed'
- : danger
- ? 'text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-950'
- : 'hover:bg-accent',
- isLoading && 'pointer-events-none'
- )}
- >
- {Icon && }
- {label}
- {shortcut && (
- {shortcut}
- )}
-
- );
-
- // Menu divider
- const MenuDivider = () => (
-
- );
-
- // Build menu items based on context
- const renderMenuItems = () => {
- if (isFile) {
- return (
- <>
- onRename?.(item)}
- />
- onDelete?.(item)}
- danger
- />
-
- onCopyPath?.(item)}
- />
- onDownload?.(item)}
- />
- >
- );
- }
-
- if (isDirectory) {
- return (
- <>
- onNewFile?.(item.path)}
- />
- onNewFolder?.(item.path)}
- />
-
- onRename?.(item)}
- />
- onDelete?.(item)}
- danger
- />
-
- onCopyPath?.(item)}
- />
- onDownload?.(item)}
- />
- >
- );
- }
-
- // Background context (empty space)
- return (
- <>
- onNewFile?.('')}
- />
- onNewFolder?.('')}
- />
-
-
- >
- );
- };
-
- return (
- <>
- {/* Trigger element */}
-
- {children}
-
-
- {/* Context menu portal */}
- {isOpen && (
-
- {isLoading ? (
-
-
-
- {t('fileTree.context.loading', 'Loading...')}
-
-
- ) : (
- renderMenuItems()
- )}
-
- )}
- >
- );
-};
-
-export default FileContextMenu;
diff --git a/src/components/GeminiStatus.jsx b/src/components/GeminiStatus.jsx
deleted file mode 100644
index b5a10e7f..00000000
--- a/src/components/GeminiStatus.jsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { cn } from '../lib/utils';
-
-function GeminiStatus({ status, onAbort, isLoading }) {
- const [elapsedTime, setElapsedTime] = useState(0);
- const [animationPhase, setAnimationPhase] = useState(0);
-
- // Update elapsed time every second
- useEffect(() => {
- if (!isLoading) {
- setElapsedTime(0);
- return;
- }
-
- const startTime = Date.now();
- const timer = setInterval(() => {
- const elapsed = Math.floor((Date.now() - startTime) / 1000);
- setElapsedTime(elapsed);
- }, 1000);
-
- return () => clearInterval(timer);
- }, [isLoading]);
-
- // Animate the status indicator
- useEffect(() => {
- if (!isLoading) return;
-
- const timer = setInterval(() => {
- setAnimationPhase(prev => (prev + 1) % 4);
- }, 500);
-
- return () => clearInterval(timer);
- }, [isLoading]);
-
- if (!isLoading) return null;
-
- // Clever action words that cycle
- const actionWords = ['Thinking', 'Processing', 'Analyzing', 'Working', 'Computing', 'Reasoning'];
- const actionIndex = Math.floor(elapsedTime / 3) % actionWords.length;
-
- // Parse status data
- const statusText = status?.text || actionWords[actionIndex];
- const canInterrupt = status?.can_interrupt !== false;
-
- // Animation characters
- const spinners = ['✻', '✹', '✸', '✶'];
- const currentSpinner = spinners[animationPhase];
-
- return (
-
-
-
-
- {/* Animated spinner */}
-
- {currentSpinner}
-
-
- {/* Status text - first line */}
-
-
- {statusText}...
- ({elapsedTime}s)
-
-
-
-
-
- {/* Interrupt button */}
- {canInterrupt && onAbort && (
-
-
-
-
- Stop
-
- )}
-
-
- );
-}
-
-export default GeminiStatus;
\ No newline at end of file
diff --git a/src/components/LoginForm.jsx b/src/components/LoginForm.jsx
deleted file mode 100644
index 1482ad7d..00000000
--- a/src/components/LoginForm.jsx
+++ /dev/null
@@ -1,112 +0,0 @@
-import React, { useState } from 'react';
-import { useAuth } from '../contexts/AuthContext';
-import { MessageSquare } from 'lucide-react';
-import { useTranslation } from 'react-i18next';
-
-const LoginForm = () => {
- const { t } = useTranslation('auth');
- const [username, setUsername] = useState('');
- const [password, setPassword] = useState('');
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState('');
-
- const { login } = useAuth();
-
- const handleSubmit = async (e) => {
- e.preventDefault();
- setError('');
-
- if (!username || !password) {
- setError(t('errors.requiredFields'));
- return;
- }
-
- setIsLoading(true);
-
- const result = await login(username, password);
-
- if (!result.success) {
- setError(result.error);
- }
-
- setIsLoading(false);
- };
-
- return (
-
-
-
- {/* Logo and Title */}
-
-
-
{t('login.title')}
-
- {t('login.description')}
-
-
-
- {/* Login Form */}
-
-
-
-
- Enter your credentials to access Claude Code UI
-
-
-
-
-
- );
-};
-
-export default LoginForm;
\ No newline at end of file
diff --git a/src/components/LoginModal.jsx b/src/components/LoginModal.jsx
deleted file mode 100644
index 9106140f..00000000
--- a/src/components/LoginModal.jsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import { X, ExternalLink, KeyRound } from 'lucide-react';
-import StandaloneShell from './standalone-shell/view/StandaloneShell';
-import { IS_PLATFORM } from '../constants/config';
-
-/**
- * Reusable login modal component for Claude, Cursor, Codex, and Gemini CLI authentication
- *
- * @param {Object} props
- * @param {boolean} props.isOpen - Whether the modal is visible
- * @param {Function} props.onClose - Callback when modal is closed
- * @param {'claude'|'cursor'|'codex'|'gemini'} props.provider - Which CLI provider to authenticate with
- * @param {Object} props.project - Project object containing name and path information
- * @param {Function} props.onComplete - Callback when login process completes (receives exitCode)
- * @param {string} props.customCommand - Optional custom command to override defaults
- * @param {boolean} props.isAuthenticated - Whether user is already authenticated (for re-auth flow)
- */
-function LoginModal({
- isOpen,
- onClose,
- provider = 'claude',
- project,
- onComplete,
- customCommand,
- isAuthenticated = false,
- isOnboarding = false
-}) {
- if (!isOpen) return null;
-
- const getCommand = () => {
- if (customCommand) return customCommand;
-
- switch (provider) {
- case 'claude':
- return isAuthenticated ? 'claude setup-token --dangerously-skip-permissions' : isOnboarding ? 'claude /exit --dangerously-skip-permissions' : 'claude /login --dangerously-skip-permissions';
- case 'cursor':
- return 'cursor-agent login';
- case 'codex':
- return IS_PLATFORM ? 'codex login --device-auth' : 'codex login';
- case 'gemini':
- // No explicit interactive login command for gemini CLI exists yet similar to Claude, so we'll just check status or instruct the user to configure `.gemini.json`
- return 'gemini status';
- default:
- return isAuthenticated ? 'claude setup-token --dangerously-skip-permissions' : isOnboarding ? 'claude /exit --dangerously-skip-permissions' : 'claude /login --dangerously-skip-permissions';
- }
- };
-
- const getTitle = () => {
- switch (provider) {
- case 'claude':
- return 'Claude CLI Login';
- case 'cursor':
- return 'Cursor CLI Login';
- case 'codex':
- return 'Codex CLI Login';
- case 'gemini':
- return 'Gemini CLI Configuration';
- default:
- return 'CLI Login';
- }
- };
-
- const handleComplete = (exitCode) => {
- if (onComplete) {
- onComplete(exitCode);
- }
- // Keep modal open so users can read login output and close explicitly.
- };
-
- return (
-
-
-
-
- {getTitle()}
-
-
-
-
-
-
- {provider === 'gemini' ? (
-
-
-
-
-
-
- Setup Gemini API Access
-
-
-
- The Gemini CLI requires an API key to function. Unlike Claude, you'll need to configure this directly in your terminal first.
-
-
-
-
-
-
- 1
-
-
-
-
-
- 2
-
-
-
Run configuration
-
Open your terminal and run:
-
- gemini config set api_key YOUR_KEY
-
-
-
-
-
-
-
- Done
-
-
- ) : (
-
- )}
-
-
-
- );
-}
-
-export default LoginModal;
diff --git a/src/components/NextTaskBanner.jsx b/src/components/NextTaskBanner.jsx
deleted file mode 100644
index 49eb941e..00000000
--- a/src/components/NextTaskBanner.jsx
+++ /dev/null
@@ -1,695 +0,0 @@
-import React, { useState } from 'react';
-import { ArrowRight, List, Clock, Flag, CheckCircle, Circle, AlertCircle, Pause, ChevronDown, ChevronUp, Plus, FileText, Settings, X, Terminal, Eye, Play, Zap, Target } from 'lucide-react';
-import { cn } from '../lib/utils';
-import { useTaskMaster } from '../contexts/TaskMasterContext';
-import { api } from '../utils/api';
-import Shell from './shell/view/Shell';
-import TaskDetail from './TaskDetail';
-
-const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => {
- const { nextTask, tasks, currentProject, isLoadingTasks, projectTaskMaster, refreshTasks, refreshProjects } = useTaskMaster();
- const [showDetails, setShowDetails] = useState(false);
- const [showTaskOptions, setShowTaskOptions] = useState(false);
- const [showCreateTaskModal, setShowCreateTaskModal] = useState(false);
- const [showTemplateSelector, setShowTemplateSelector] = useState(false);
- const [showCLI, setShowCLI] = useState(false);
- const [showTaskDetail, setShowTaskDetail] = useState(false);
- const [isLoading, setIsLoading] = useState(false);
-
- // Handler functions
- const handleInitializeTaskMaster = async () => {
- if (!currentProject) return;
-
- setIsLoading(true);
- try {
- const response = await api.taskmaster.init(currentProject.name);
- if (response.ok) {
- await refreshProjects();
- setShowTaskOptions(false);
- } else {
- const error = await response.json();
- console.error('Failed to initialize TaskMaster:', error);
- alert(`Failed to initialize TaskMaster: ${error.message}`);
- }
- } catch (error) {
- console.error('Error initializing TaskMaster:', error);
- alert('Error initializing TaskMaster. Please try again.');
- } finally {
- setIsLoading(false);
- }
- };
-
- const handleCreateManualTask = () => {
- setShowCreateTaskModal(true);
- setShowTaskOptions(false);
- };
-
- const handleParsePRD = () => {
- setShowTemplateSelector(true);
- setShowTaskOptions(false);
- };
-
- // Don't show if no project or still loading
- if (!currentProject || isLoadingTasks) {
- return null;
- }
-
- let bannerContent;
-
- // Show setup message only if no tasks exist AND TaskMaster is not configured
- if ((!tasks || tasks.length === 0) && !projectTaskMaster?.hasTaskmaster) {
- bannerContent = (
-
-
-
-
-
-
- TaskMaster AI is not configured
-
-
-
-
-
-
- setShowTaskOptions(!showTaskOptions)}
- className="text-xs px-2 py-1 bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors flex items-center gap-1"
- >
-
- Initialize TaskMaster AI
-
-
-
-
- {showTaskOptions && (
-
- {!projectTaskMaster?.hasTaskmaster && (
-
-
- 🎯 What is TaskMaster?
-
-
-
• AI-Powered Task Management: Break complex projects into manageable subtasks
-
• PRD Templates: Generate tasks from Product Requirements Documents
-
• Dependency Tracking: Understand task relationships and execution order
-
• Progress Visualization: Kanban boards and detailed task analytics
-
• CLI Integration: Use taskmaster commands for advanced workflows
-
-
- )}
-
- {!projectTaskMaster?.hasTaskmaster ? (
-
setShowCLI(true)}
- >
-
- Initialize TaskMaster
-
- ) : (
- <>
-
- Add more tasks: Create additional tasks manually or generate them from a PRD template
-
-
-
- Create a new task manually
-
-
-
- {isLoading ? 'Parsing...' : 'Generate tasks from PRD template'}
-
- >
- )}
-
-
- )}
-
- );
- } else if (nextTask) {
- // Show next task if available
- bannerContent = (
-
-
-
-
-
-
-
-
Task {nextTask.id}
- {nextTask.priority === 'high' && (
-
-
-
- )}
- {nextTask.priority === 'medium' && (
-
-
-
- )}
- {nextTask.priority === 'low' && (
-
-
-
- )}
-
-
- {nextTask.title}
-
-
-
-
-
onStartTask?.()}
- className="text-xs px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white rounded-md font-medium transition-colors shadow-sm flex items-center gap-1"
- >
-
- Start Task
-
-
setShowTaskDetail(true)}
- className="text-xs px-2 py-1.5 border border-slate-300 dark:border-slate-600 hover:bg-slate-100 dark:hover:bg-slate-800 text-slate-600 dark:text-slate-300 rounded-md transition-colors flex items-center gap-1"
- title="View task details"
- >
-
-
- {onShowAllTasks && (
-
-
-
- )}
-
-
-
-
- );
- } else if (tasks && tasks.length > 0) {
- // Show completion message only if there are tasks and all are done
- const completedTasks = tasks.filter(task => task.status === 'done').length;
- const totalTasks = tasks.length;
-
- bannerContent = (
-
-
-
-
-
- {completedTasks === totalTasks ? "All done! 🎉" : "No pending tasks"}
-
-
-
-
- {completedTasks}/{totalTasks}
-
-
- Review
-
-
-
-
- );
- } else {
- // TaskMaster is configured but no tasks exist - don't show anything in chat
- bannerContent = null;
- }
-
- return (
- <>
- {bannerContent}
-
- {/* Create Task Modal */}
- {showCreateTaskModal && (
- setShowCreateTaskModal(false)}
- onTaskCreated={() => {
- refreshTasks();
- setShowCreateTaskModal(false);
- }}
- />
- )}
-
- {/* Template Selector Modal */}
- {showTemplateSelector && (
- setShowTemplateSelector(false)}
- onTemplateApplied={() => {
- refreshTasks();
- setShowTemplateSelector(false);
- }}
- />
- )}
-
- {/* TaskMaster CLI Setup Modal */}
- {showCLI && (
-
-
- {/* Modal Header */}
-
-
-
-
-
-
-
TaskMaster Setup
-
Interactive CLI for {currentProject?.displayName}
-
-
-
setShowCLI(false)}
- className="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800"
- >
-
-
-
-
- {/* Terminal Container */}
-
-
- {/* Modal Footer */}
-
-
-
- TaskMaster initialization will start automatically
-
-
setShowCLI(false)}
- className="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors"
- >
- Close
-
-
-
-
-
- )}
-
- {/* Task Detail Modal */}
- {showTaskDetail && nextTask && (
- setShowTaskDetail(false)}
- onStatusChange={() => refreshTasks?.()}
- onTaskClick={null} // Disable dependency navigation in NextTaskBanner for now
- />
- )}
- >
- );
-};
-
-// Simple Create Task Modal Component
-const CreateTaskModal = ({ currentProject, onClose, onTaskCreated }) => {
- const [formData, setFormData] = useState({
- title: '',
- description: '',
- priority: 'medium',
- useAI: false,
- prompt: ''
- });
- const [isSubmitting, setIsSubmitting] = useState(false);
-
- const handleSubmit = async (e) => {
- e.preventDefault();
- if (!currentProject) return;
-
- setIsSubmitting(true);
- try {
- const taskData = formData.useAI
- ? { prompt: formData.prompt, priority: formData.priority }
- : { title: formData.title, description: formData.description, priority: formData.priority };
-
- const response = await api.taskmaster.addTask(currentProject.name, taskData);
-
- if (response.ok) {
- onTaskCreated();
- } else {
- const error = await response.json();
- console.error('Failed to create task:', error);
- alert(`Failed to create task: ${error.message}`);
- }
- } catch (error) {
- console.error('Error creating task:', error);
- alert('Error creating task. Please try again.');
- } finally {
- setIsSubmitting(false);
- }
- };
-
- return (
-
-
-
-
Create New Task
-
-
-
-
-
-
-
-
- );
-};
-
-// Template Selector Modal Component
-const TemplateSelector = ({ currentProject, onClose, onTemplateApplied }) => {
- const [templates, setTemplates] = useState([]);
- const [selectedTemplate, setSelectedTemplate] = useState(null);
- const [customizations, setCustomizations] = useState({});
- const [fileName, setFileName] = useState('prd.txt');
- const [isLoading, setIsLoading] = useState(true);
- const [isApplying, setIsApplying] = useState(false);
- const [step, setStep] = useState('select'); // 'select', 'customize', 'generate'
-
- useEffect(() => {
- const loadTemplates = async () => {
- try {
- const response = await api.taskmaster.getTemplates();
- if (response.ok) {
- const data = await response.json();
- setTemplates(data.templates);
- }
- } catch (error) {
- console.error('Error loading templates:', error);
- } finally {
- setIsLoading(false);
- }
- };
-
- loadTemplates();
- }, []);
-
- const handleSelectTemplate = (template) => {
- setSelectedTemplate(template);
- // Find placeholders in template content
- const placeholders = template.content.match(/\[([^\]]+)\]/g) || [];
- const uniquePlaceholders = [...new Set(placeholders.map(p => p.slice(1, -1)))];
-
- const initialCustomizations = {};
- uniquePlaceholders.forEach(placeholder => {
- initialCustomizations[placeholder] = '';
- });
-
- setCustomizations(initialCustomizations);
- setStep('customize');
- };
-
- const handleApplyTemplate = async () => {
- if (!selectedTemplate || !currentProject) return;
-
- setIsApplying(true);
- try {
- // Apply template
- const applyResponse = await api.taskmaster.applyTemplate(currentProject.name, {
- templateId: selectedTemplate.id,
- fileName,
- customizations
- });
-
- if (!applyResponse.ok) {
- const error = await applyResponse.json();
- throw new Error(error.message || 'Failed to apply template');
- }
-
- // Parse PRD to generate tasks
- const parseResponse = await api.taskmaster.parsePRD(currentProject.name, {
- fileName,
- numTasks: 10
- });
-
- if (!parseResponse.ok) {
- const error = await parseResponse.json();
- throw new Error(error.message || 'Failed to generate tasks');
- }
-
- setStep('generate');
- setTimeout(() => {
- onTemplateApplied();
- }, 2000);
-
- } catch (error) {
- console.error('Error applying template:', error);
- alert(`Error: ${error.message}`);
- setIsApplying(false);
- }
- };
-
- if (isLoading) {
- return (
-
-
-
-
-
Loading templates...
-
-
-
- );
- }
-
- return (
-
-
-
-
- {step === 'select' ? 'Select PRD Template' :
- step === 'customize' ? 'Customize Template' :
- 'Generating Tasks'}
-
-
-
-
-
-
- {step === 'select' && (
-
- {templates.map((template) => (
-
handleSelectTemplate(template)}
- >
-
-
-
{template.name}
-
{template.description}
-
- {template.category}
-
-
-
-
-
- ))}
-
- )}
-
- {step === 'customize' && selectedTemplate && (
-
-
-
- File Name
-
- setFileName(e.target.value)}
- className="w-full p-2 border border-gray-300 dark:border-gray-600 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
- placeholder="prd.txt"
- />
-
-
- {Object.keys(customizations).length > 0 && (
-
-
- Customize Template
-
-
- {Object.entries(customizations).map(([key, value]) => (
-
-
- {key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())}
-
- setCustomizations(prev => ({ ...prev, [key]: e.target.value }))}
- className="w-full p-2 border border-gray-300 dark:border-gray-600 rounded text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
- placeholder={`Enter ${key.toLowerCase()}`}
- />
-
- ))}
-
-
- )}
-
-
- setStep('select')}
- className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"
- >
- Back
-
-
- {isApplying ? 'Applying...' : 'Apply & Generate Tasks'}
-
-
-
- )}
-
- {step === 'generate' && (
-
-
-
-
-
- Template Applied Successfully!
-
-
- Your PRD has been created and tasks are being generated...
-
-
- )}
-
-
- );
-};
-
-export default NextTaskBanner;
\ No newline at end of file
diff --git a/src/components/Onboarding.jsx b/src/components/Onboarding.jsx
deleted file mode 100644
index 15fed04c..00000000
--- a/src/components/Onboarding.jsx
+++ /dev/null
@@ -1,567 +0,0 @@
-import React, { useState, useEffect, useRef } from 'react';
-import { ChevronRight, ChevronLeft, Check, GitBranch, User, Mail, LogIn, ExternalLink, Loader2 } from 'lucide-react';
-import SessionProviderLogo from './llm-logo-provider/SessionProviderLogo';
-import LoginModal from './LoginModal';
-import { authenticatedFetch } from '../utils/api';
-import { useAuth } from '../contexts/AuthContext';
-import { IS_PLATFORM } from '../constants/config';
-
-const Onboarding = ({ onComplete }) => {
- const [currentStep, setCurrentStep] = useState(0);
- const [gitName, setGitName] = useState('');
- const [gitEmail, setGitEmail] = useState('');
- const [isSubmitting, setIsSubmitting] = useState(false);
- const [error, setError] = useState('');
-
- const [activeLoginProvider, setActiveLoginProvider] = useState(null);
- const [selectedProject] = useState({ name: 'default', fullPath: IS_PLATFORM ? '/workspace' : '' });
-
- const [claudeAuthStatus, setClaudeAuthStatus] = useState({
- authenticated: false,
- email: null,
- loading: true,
- error: null
- });
-
- const [cursorAuthStatus, setCursorAuthStatus] = useState({
- authenticated: false,
- email: null,
- loading: true,
- error: null
- });
-
- const [codexAuthStatus, setCodexAuthStatus] = useState({
- authenticated: false,
- email: null,
- loading: true,
- error: null
- });
-
- const [geminiAuthStatus, setGeminiAuthStatus] = useState({
- authenticated: false,
- email: null,
- loading: true,
- error: null
- });
-
- const { user } = useAuth();
-
- const prevActiveLoginProviderRef = useRef(undefined);
-
- useEffect(() => {
- loadGitConfig();
- }, []);
-
- const loadGitConfig = async () => {
- try {
- const response = await authenticatedFetch('/api/user/git-config');
- if (response.ok) {
- const data = await response.json();
- if (data.gitName) setGitName(data.gitName);
- if (data.gitEmail) setGitEmail(data.gitEmail);
- }
- } catch (error) {
- console.error('Error loading git config:', error);
- }
- };
-
- useEffect(() => {
- const prevProvider = prevActiveLoginProviderRef.current;
- prevActiveLoginProviderRef.current = activeLoginProvider;
-
- const isInitialMount = prevProvider === undefined;
- const isModalClosing = prevProvider !== null && activeLoginProvider === null;
-
- if (isInitialMount || isModalClosing) {
- checkClaudeAuthStatus();
- checkCursorAuthStatus();
- checkCodexAuthStatus();
- checkGeminiAuthStatus();
- }
- }, [activeLoginProvider]);
-
- const checkProviderAuthStatus = async (provider, setter) => {
- try {
- const response = await authenticatedFetch(`/api/cli/${provider}/status`);
- if (response.ok) {
- const data = await response.json();
- setter({
- authenticated: data.authenticated,
- email: data.email,
- loading: false,
- error: data.error || null
- });
- } else {
- setter({
- authenticated: false,
- email: null,
- loading: false,
- error: 'Failed to check authentication status'
- });
- }
- } catch (error) {
- console.error(`Error checking ${provider} auth status:`, error);
- setter({
- authenticated: false,
- email: null,
- loading: false,
- error: error.message
- });
- }
- };
-
- const checkClaudeAuthStatus = () => checkProviderAuthStatus('claude', setClaudeAuthStatus);
- const checkCursorAuthStatus = () => checkProviderAuthStatus('cursor', setCursorAuthStatus);
- const checkCodexAuthStatus = () => checkProviderAuthStatus('codex', setCodexAuthStatus);
- const checkGeminiAuthStatus = () => checkProviderAuthStatus('gemini', setGeminiAuthStatus);
-
- const handleClaudeLogin = () => setActiveLoginProvider('claude');
- const handleCursorLogin = () => setActiveLoginProvider('cursor');
- const handleCodexLogin = () => setActiveLoginProvider('codex');
- const handleGeminiLogin = () => setActiveLoginProvider('gemini');
-
- const handleLoginComplete = (exitCode) => {
- if (exitCode === 0) {
- if (activeLoginProvider === 'claude') {
- checkClaudeAuthStatus();
- } else if (activeLoginProvider === 'cursor') {
- checkCursorAuthStatus();
- } else if (activeLoginProvider === 'codex') {
- checkCodexAuthStatus();
- } else if (activeLoginProvider === 'gemini') {
- checkGeminiAuthStatus();
- }
- }
- };
-
- const handleNextStep = async () => {
- setError('');
-
- // Step 0: Git config validation and submission
- if (currentStep === 0) {
- if (!gitName.trim() || !gitEmail.trim()) {
- setError('Both git name and email are required');
- return;
- }
-
- // Validate email format
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
- if (!emailRegex.test(gitEmail)) {
- setError('Please enter a valid email address');
- return;
- }
-
- setIsSubmitting(true);
- try {
- // Save git config to backend (which will also apply git config --global)
- const response = await authenticatedFetch('/api/user/git-config', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ gitName, gitEmail })
- });
-
- if (!response.ok) {
- const data = await response.json();
- throw new Error(data.error || 'Failed to save git configuration');
- }
-
- setCurrentStep(currentStep + 1);
- } catch (err) {
- setError(err.message);
- } finally {
- setIsSubmitting(false);
- }
- return;
- }
-
- setCurrentStep(currentStep + 1);
- };
-
- const handlePrevStep = () => {
- setError('');
- setCurrentStep(currentStep - 1);
- };
-
- const handleFinish = async () => {
- setIsSubmitting(true);
- setError('');
-
- try {
- const response = await authenticatedFetch('/api/user/complete-onboarding', {
- method: 'POST'
- });
-
- if (!response.ok) {
- const data = await response.json();
- throw new Error(data.error || 'Failed to complete onboarding');
- }
-
- if (onComplete) {
- onComplete();
- }
- } catch (err) {
- setError(err.message);
- } finally {
- setIsSubmitting(false);
- }
- };
-
- const steps = [
- {
- title: 'Git Configuration',
- description: 'Set up your git identity for commits',
- icon: GitBranch,
- required: true
- },
- {
- title: 'Connect Agents',
- description: 'Connect your AI coding assistants',
- icon: LogIn,
- required: false
- }
- ];
-
- const renderStepContent = () => {
- switch (currentStep) {
- case 0:
- return (
-
-
-
-
-
-
Git Configuration
-
- Configure your git identity to ensure proper attribution for your commits
-
-
-
-
-
-
-
- Git Name *
-
-
setGitName(e.target.value)}
- className="w-full px-4 py-3 border border-border rounded-lg bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
- placeholder="John Doe"
- required
- disabled={isSubmitting}
- />
-
- This will be used as: git config --global user.name
-
-
-
-
-
-
- Git Email *
-
-
setGitEmail(e.target.value)}
- className="w-full px-4 py-3 border border-border rounded-lg bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
- placeholder="john@example.com"
- required
- disabled={isSubmitting}
- />
-
- This will be used as: git config --global user.email
-
-
-
-
- );
-
- case 1:
- return (
-
-
-
Connect Your AI Agents
-
- Login to one or more AI coding assistants. All are optional.
-
-
-
- {/* Agent Cards Grid */}
-
- {/* Claude */}
-
-
-
-
-
-
-
-
- Claude Code
- {claudeAuthStatus.authenticated && }
-
-
- {claudeAuthStatus.loading ? 'Checking...' :
- claudeAuthStatus.authenticated ? claudeAuthStatus.email || 'Connected' : 'Not connected'}
-
-
-
- {!claudeAuthStatus.authenticated && !claudeAuthStatus.loading && (
-
- Login
-
- )}
-
-
-
- {/* Cursor */}
-
-
-
-
-
-
-
-
- Cursor
- {cursorAuthStatus.authenticated && }
-
-
- {cursorAuthStatus.loading ? 'Checking...' :
- cursorAuthStatus.authenticated ? cursorAuthStatus.email || 'Connected' : 'Not connected'}
-
-
-
- {!cursorAuthStatus.authenticated && !cursorAuthStatus.loading && (
-
- Login
-
- )}
-
-
-
- {/* Codex */}
-
-
-
-
-
-
-
-
- OpenAI Codex
- {codexAuthStatus.authenticated && }
-
-
- {codexAuthStatus.loading ? 'Checking...' :
- codexAuthStatus.authenticated ? codexAuthStatus.email || 'Connected' : 'Not connected'}
-
-
-
- {!codexAuthStatus.authenticated && !codexAuthStatus.loading && (
-
- Login
-
- )}
-
-
-
- {/* Gemini */}
-
-
-
-
-
-
-
-
- Gemini
- {geminiAuthStatus.authenticated && }
-
-
- {geminiAuthStatus.loading ? 'Checking...' :
- geminiAuthStatus.authenticated ? geminiAuthStatus.email || 'Connected' : 'Not connected'}
-
-
-
- {!geminiAuthStatus.authenticated && !geminiAuthStatus.loading && (
-
- Login
-
- )}
-
-
-
-
-
-
You can configure these later in Settings.
-
-
- );
-
- default:
- return null;
- }
- };
-
- const isStepValid = () => {
- switch (currentStep) {
- case 0:
- return gitName.trim() && gitEmail.trim() && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(gitEmail);
- case 1:
- return true;
- default:
- return false;
- }
- };
-
- return (
- <>
-
-
- {/* Progress Steps */}
-
-
- {steps.map((step, index) => (
-
-
-
- {index < currentStep ? (
-
- ) : typeof step.icon === 'function' ? (
-
- ) : (
-
- )}
-
-
-
- {step.title}
-
- {step.required && (
-
Required
- )}
-
-
- {index < steps.length - 1 && (
-
- )}
-
- ))}
-
-
-
- {/* Main Card */}
-
- {renderStepContent()}
-
- {/* Error Message */}
- {error && (
-
- )}
-
- {/* Navigation Buttons */}
-
-
-
- Previous
-
-
-
- {currentStep < steps.length - 1 ? (
-
- {isSubmitting ? (
- <>
-
- Saving...
- >
- ) : (
- <>
- Next
-
- >
- )}
-
- ) : (
-
- {isSubmitting ? (
- <>
-
- Completing...
- >
- ) : (
- <>
-
- Complete Setup
- >
- )}
-
- )}
-
-
-
-
-
-
- {activeLoginProvider && (
- setActiveLoginProvider(null)}
- provider={activeLoginProvider}
- project={selectedProject}
- onComplete={handleLoginComplete}
- isOnboarding={true}
- />
- )}
- >
- );
-};
-
-export default Onboarding;
diff --git a/src/components/PRDEditor.jsx b/src/components/PRDEditor.jsx
deleted file mode 100644
index 3f1ff358..00000000
--- a/src/components/PRDEditor.jsx
+++ /dev/null
@@ -1,871 +0,0 @@
-import React, { useState, useEffect, useRef } from 'react';
-import CodeMirror from '@uiw/react-codemirror';
-import { markdown } from '@codemirror/lang-markdown';
-import { oneDark } from '@codemirror/theme-one-dark';
-import { EditorView } from '@codemirror/view';
-import { X, Save, Download, Maximize2, Minimize2, Eye, FileText, Sparkles, AlertTriangle } from 'lucide-react';
-import { cn } from '../lib/utils';
-import { api, authenticatedFetch } from '../utils/api';
-
-const PRDEditor = ({
- file,
- onClose,
- projectPath,
- project, // Add project object
- initialContent = '',
- isNewFile = false,
- onSave
-}) => {
- const [content, setContent] = useState(initialContent);
- const [loading, setLoading] = useState(!isNewFile);
- const [saving, setSaving] = useState(false);
- const [isFullscreen, setIsFullscreen] = useState(false);
- const [isDarkMode, setIsDarkMode] = useState(true);
- const [saveSuccess, setSaveSuccess] = useState(false);
- const [previewMode, setPreviewMode] = useState(false);
- const [wordWrap, setWordWrap] = useState(true); // Default to true for markdown
- const [fileName, setFileName] = useState('');
- const [showGenerateModal, setShowGenerateModal] = useState(false);
- const [showOverwriteConfirm, setShowOverwriteConfirm] = useState(false);
- const [existingPRDs, setExistingPRDs] = useState([]);
-
- const editorRef = useRef(null);
-
- const PRD_TEMPLATE = `# Product Requirements Document - Example Project
-
-## 1. Overview
-**Product Name:** AI-Powered Task Manager
-**Version:** 1.0
-**Date:** 2024-12-27
-**Author:** Development Team
-
-This document outlines the requirements for building an AI-powered task management application that integrates with development workflows and provides intelligent task breakdown and prioritization.
-
-## 2. Objectives
-- Create an intuitive task management system that works seamlessly with developer tools
-- Provide AI-powered task generation from high-level requirements
-- Enable real-time collaboration and progress tracking
-- Integrate with popular development environments (VS Code, Cursor, etc.)
-
-### Success Metrics
-- User adoption rate > 80% within development teams
-- Task completion rate improvement of 25%
-- Time-to-delivery reduction of 15%
-
-## 3. User Stories
-
-### Core Functionality
-- As a project manager, I want to create PRDs that automatically generate detailed tasks so I can save time on project planning
-- As a developer, I want to see my next task clearly highlighted so I can maintain focus
-- As a team lead, I want to track progress across multiple projects so I can provide accurate status updates
-- As a developer, I want tasks to be broken down into implementable subtasks so I can work more efficiently
-
-### AI Integration
-- As a user, I want to describe a feature in natural language and get detailed implementation tasks so I can start working immediately
-- As a project manager, I want the AI to analyze task complexity and suggest appropriate time estimates
-- As a developer, I want intelligent task prioritization based on dependencies and deadlines
-
-### Collaboration
-- As a team member, I want to see real-time updates when tasks are completed so I can coordinate my work
-- As a stakeholder, I want to view project progress through intuitive dashboards
-- As a developer, I want to add implementation notes to tasks for future reference
-
-## 4. Functional Requirements
-
-### Task Management
-- Create, edit, and delete tasks with rich metadata (priority, status, dependencies, estimates)
-- Hierarchical task structure with subtasks and sub-subtasks
-- Real-time status updates and progress tracking
-- Dependency management with circular dependency detection
-- Bulk operations (move, update status, assign)
-
-### AI Features
-- Natural language PRD parsing to generate structured tasks
-- Intelligent task breakdown with complexity analysis
-- Automated subtask generation with implementation details
-- Smart dependency suggestion
-- Progress prediction based on historical data
-
-### Integration Features
-- VS Code/Cursor extension for in-editor task management
-- Git integration for linking commits to tasks
-- API for third-party tool integration
-- Webhook support for external notifications
-- CLI tool for command-line task management
-
-### User Interface
-- Responsive web application (desktop and mobile)
-- Multiple view modes (Kanban, list, calendar)
-- Dark/light theme support
-- Drag-and-drop task organization
-- Advanced filtering and search capabilities
-- Keyboard shortcuts for power users
-
-## 5. Technical Requirements
-
-### Frontend
-- React.js with TypeScript for type safety
-- Modern UI framework (Tailwind CSS)
-- State management (Context API or Redux)
-- Real-time updates via WebSockets
-- Progressive Web App (PWA) support
-- Accessibility compliance (WCAG 2.1 AA)
-
-### Backend
-- Node.js with Express.js framework
-- RESTful API design with OpenAPI documentation
-- Real-time communication via Socket.io
-- Background job processing
-- Rate limiting and security middleware
-
-### AI Integration
-- Integration with multiple AI providers (OpenAI, Anthropic, etc.)
-- Fallback model support
-- Context-aware prompt engineering
-- Token usage optimization
-- Model response caching
-
-### Database
-- Primary: PostgreSQL for relational data
-- Cache: Redis for session management and real-time features
-- Full-text search capabilities
-- Database migrations and seeding
-- Backup and recovery procedures
-
-### Infrastructure
-- Docker containerization
-- Cloud deployment (AWS/GCP/Azure)
-- Auto-scaling capabilities
-- Monitoring and logging (structured logging)
-- CI/CD pipeline with automated testing
-
-## 6. Non-Functional Requirements
-
-### Performance
-- Page load time < 2 seconds
-- API response time < 500ms for 95% of requests
-- Support for 1000+ concurrent users
-- Efficient handling of large task lists (10,000+ tasks)
-
-### Security
-- JWT-based authentication with refresh tokens
-- Role-based access control (RBAC)
-- Data encryption at rest and in transit
-- Regular security audits and penetration testing
-- GDPR and privacy compliance
-
-### Reliability
-- 99.9% uptime SLA
-- Graceful error handling and recovery
-- Data backup every 6 hours with point-in-time recovery
-- Disaster recovery plan with RTO < 4 hours
-
-### Scalability
-- Horizontal scaling for both frontend and backend
-- Database read replicas for query optimization
-- CDN for static asset delivery
-- Microservices architecture for future expansion
-
-## 7. User Experience Design
-
-### Information Architecture
-- Intuitive navigation with breadcrumbs
-- Context-aware menus and actions
-- Progressive disclosure of complex features
-- Consistent design patterns throughout
-
-### Interaction Design
-- Smooth animations and transitions
-- Immediate feedback for user actions
-- Undo/redo functionality for critical operations
-- Smart defaults and auto-save features
-
-### Visual Design
-- Modern, clean interface with plenty of whitespace
-- Consistent color scheme and typography
-- Clear visual hierarchy with proper contrast ratios
-- Iconography that supports comprehension
-
-## 8. Integration Requirements
-
-### Development Tools
-- VS Code extension with task panel and quick actions
-- Cursor IDE integration with AI task suggestions
-- Terminal CLI for command-line workflow
-- Browser extension for web-based tools
-
-### Third-Party Services
-- GitHub/GitLab integration for issue sync
-- Slack/Discord notifications
-- Calendar integration (Google Calendar, Outlook)
-- Time tracking tools (Toggl, Harvest)
-
-### APIs and Webhooks
-- RESTful API with comprehensive documentation
-- GraphQL endpoint for complex queries
-- Webhook system for external integrations
-- SDK development for major programming languages
-
-## 9. Implementation Phases
-
-### Phase 1: Core MVP (8-10 weeks)
-- Basic task management (CRUD operations)
-- Simple AI task generation
-- Web interface with essential features
-- User authentication and basic permissions
-
-### Phase 2: Enhanced Features (6-8 weeks)
-- Advanced AI features (complexity analysis, subtask generation)
-- Real-time collaboration
-- Mobile-responsive design
-- Integration with one development tool (VS Code)
-
-### Phase 3: Enterprise Features (4-6 weeks)
-- Advanced user management and permissions
-- API and webhook system
-- Performance optimization
-- Comprehensive testing and security audit
-
-### Phase 4: Ecosystem Expansion (4-6 weeks)
-- Additional tool integrations
-- Mobile app development
-- Advanced analytics and reporting
-- Third-party marketplace preparation
-
-## 10. Risk Assessment
-
-### Technical Risks
-- AI model reliability and cost management
-- Real-time synchronization complexity
-- Database performance with large datasets
-- Integration complexity with multiple tools
-
-### Business Risks
-- User adoption in competitive market
-- AI provider dependency
-- Data privacy and security concerns
-- Feature scope creep and timeline delays
-
-### Mitigation Strategies
-- Implement robust error handling and fallback systems
-- Develop comprehensive testing strategy
-- Create detailed documentation and user guides
-- Establish clear project scope and change management process
-
-## 11. Success Criteria
-
-### Development Milestones
-- Alpha version with core features completed
-- Beta version with selected user group feedback
-- Production-ready version with full feature set
-- Post-launch iterations based on user feedback
-
-### Business Metrics
-- User engagement and retention rates
-- Task completion and productivity metrics
-- Customer satisfaction scores (NPS > 50)
-- Revenue targets and subscription growth
-
-## 12. Appendices
-
-### Glossary
-- **PRD**: Product Requirements Document
-- **AI**: Artificial Intelligence
-- **CRUD**: Create, Read, Update, Delete
-- **API**: Application Programming Interface
-- **CI/CD**: Continuous Integration/Continuous Deployment
-
-### References
-- Industry best practices for task management
-- AI integration patterns and examples
-- Security and compliance requirements
-- Performance benchmarking data
-
----
-
-**Document Control:**
-- Version: 1.0
-- Last Updated: December 27, 2024
-- Next Review: January 15, 2025
-- Approved By: Product Owner, Technical Lead`;
-
- // Initialize filename and load content
- useEffect(() => {
- const initializeEditor = async () => {
- // Set initial filename
- if (file?.name) {
- setFileName(file.name.replace(/\.(txt|md)$/, '')); // Remove extension for editing
- } else if (isNewFile) {
- // Generate default filename based on current date
- const now = new Date();
- const dateStr = now.toISOString().split('T')[0]; // YYYY-MM-DD
- setFileName(`prd-${dateStr}`);
- }
-
- // Load content
- if (isNewFile) {
- setContent(PRD_TEMPLATE);
- setLoading(false);
- return;
- }
-
- // If content is directly provided (for existing PRDs loaded from API)
- if (file.content) {
- setContent(file.content);
- setLoading(false);
- return;
- }
-
- // Fallback to loading from file path (legacy support)
- try {
- setLoading(true);
-
- const response = await api.readFile(file.projectName, file.path);
-
- if (!response.ok) {
- throw new Error(`Failed to load file: ${response.status} ${response.statusText}`);
- }
-
- const data = await response.json();
- setContent(data.content || PRD_TEMPLATE);
- } catch (error) {
- console.error('Error loading PRD file:', error);
- setContent(`# Error Loading PRD\n\nError: ${error.message}\n\nFile: ${file?.name || 'New PRD'}\nPath: ${file?.path || 'Not saved yet'}\n\n${PRD_TEMPLATE}`);
- } finally {
- setLoading(false);
- }
- };
-
- initializeEditor();
- }, [file, projectPath, isNewFile]);
-
- // Fetch existing PRDs to check for conflicts
- useEffect(() => {
- const fetchExistingPRDs = async () => {
- if (!project?.name) {
- console.log('No project name available:', project);
- return;
- }
-
- try {
- console.log('Fetching PRDs for project:', project.name);
- const response = await api.get(`/taskmaster/prd/${encodeURIComponent(project.name)}`);
- if (response.ok) {
- const data = await response.json();
- console.log('Fetched existing PRDs:', data.prds);
- setExistingPRDs(data.prds || []);
- } else {
- console.log('Failed to fetch PRDs:', response.status, response.statusText);
- }
- } catch (error) {
- console.error('Error fetching existing PRDs:', error);
- }
- };
-
- fetchExistingPRDs();
- }, [project?.name]);
-
- const handleSave = async () => {
- if (!content.trim()) {
- alert('Please add content before saving.');
- return;
- }
-
- if (!fileName.trim()) {
- alert('Please provide a filename for the PRD.');
- return;
- }
-
- // Check if file already exists
- const fullFileName = fileName.endsWith('.txt') || fileName.endsWith('.md') ? fileName : `${fileName}.txt`;
- const existingFile = existingPRDs.find(prd => prd.name === fullFileName);
-
- console.log('Save check:', {
- fullFileName,
- existingPRDs,
- existingFile,
- isExisting: file?.isExisting,
- fileObject: file,
- shouldShowModal: existingFile && !file?.isExisting
- });
-
- if (existingFile && !file?.isExisting) {
- console.log('Showing overwrite confirmation modal');
- // Show confirmation modal for overwrite
- setShowOverwriteConfirm(true);
- return;
- }
-
- await performSave();
- };
-
- const performSave = async () => {
- setSaving(true);
- try {
- // Ensure filename has .txt extension
- const fullFileName = fileName.endsWith('.txt') || fileName.endsWith('.md') ? fileName : `${fileName}.txt`;
-
- const response = await authenticatedFetch(`/api/taskmaster/prd/${encodeURIComponent(project?.name)}`, {
- method: 'POST',
- body: JSON.stringify({
- fileName: fullFileName,
- content
- })
- });
-
- if (!response.ok) {
- const errorData = await response.json();
- throw new Error(errorData.message || `Save failed: ${response.status}`);
- }
-
- // Show success feedback
- setSaveSuccess(true);
- setTimeout(() => setSaveSuccess(false), 2000);
-
- // Update existing PRDs list
- const response2 = await api.get(`/taskmaster/prd/${encodeURIComponent(project.name)}`);
- if (response2.ok) {
- const data = await response2.json();
- setExistingPRDs(data.prds || []);
- }
-
- // Call the onSave callback if provided (for UI updates)
- if (onSave) {
- await onSave();
- }
-
- } catch (error) {
- console.error('Error saving PRD:', error);
- alert(`Error saving PRD: ${error.message}`);
- } finally {
- setSaving(false);
- }
- };
-
- const handleDownload = () => {
- const blob = new Blob([content], { type: 'text/markdown' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- const downloadFileName = fileName ? `${fileName}.txt` : 'prd.txt';
- a.download = downloadFileName;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- };
-
- const handleGenerateTasks = async () => {
- if (!content.trim()) {
- alert('Please add content to the PRD before generating tasks.');
- return;
- }
-
- // Show AI-first modal instead of simple confirm
- setShowGenerateModal(true);
- };
-
-
- const toggleFullscreen = () => {
- setIsFullscreen(!isFullscreen);
- };
-
- // Handle keyboard shortcuts
- useEffect(() => {
- const handleKeyDown = (e) => {
- if (e.ctrlKey || e.metaKey) {
- if (e.key === 's') {
- e.preventDefault();
- handleSave();
- } else if (e.key === 'Escape') {
- e.preventDefault();
- onClose();
- }
- }
- };
-
- document.addEventListener('keydown', handleKeyDown);
- return () => document.removeEventListener('keydown', handleKeyDown);
- }, [content]);
-
- // Simple markdown to HTML converter for preview
- const renderMarkdown = (markdown) => {
- return markdown
- .replace(/^### (.*$)/gim, '$1 ')
- .replace(/^## (.*$)/gim, '$1 ')
- .replace(/^# (.*$)/gim, '$1 ')
- .replace(/\*\*(.*)\*\*/gim, '$1 ')
- .replace(/\*(.*)\*/gim, '$1 ')
- .replace(/^\- (.*$)/gim, '$1 ')
- .replace(/(.*<\/li>)/gims, '')
- .replace(/\n\n/gim, '')
- .replace(/^(?!<[h|u|l])(.*$)/gim, '
$1
')
- .replace(/<\/ul>\s*/gim, '');
- };
-
- if (loading) {
- return (
-
- );
- }
-
- return (
-
-
- {/* Header */}
-
-
-
-
-
-
- {/* Mobile: Stack filename and tags vertically for more space */}
-
- {/* Filename input row - full width on mobile */}
-
-
- {
- // Remove invalid filename characters
- const sanitizedValue = e.target.value.replace(/[<>:"/\\|?*]/g, '');
- setFileName(sanitizedValue);
- }}
- className="font-medium text-gray-900 dark:text-white bg-transparent border-none outline-none min-w-0 flex-1 text-base sm:text-sm placeholder-gray-400 dark:placeholder-gray-500"
- placeholder="Enter PRD filename"
- maxLength={100}
- />
- .txt
-
-
document.querySelector('input[placeholder="Enter PRD filename"]')?.focus()}
- className="p-1 text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors"
- title="Click to edit filename"
- >
-
-
-
-
-
-
- {/* Tags row - moves to second line on mobile for more filename space */}
-
-
- 📋 PRD
-
- {isNewFile && (
-
- ✨ New
-
- )}
-
-
-
- {/* Description - smaller on mobile */}
-
- Product Requirements Document
-
-
-
-
-
-
setPreviewMode(!previewMode)}
- className={cn(
- 'p-2 md:p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800',
- 'min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 flex items-center justify-center',
- previewMode
- ? 'text-purple-600 dark:text-purple-400 bg-purple-50 dark:bg-purple-900'
- : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'
- )}
- title={previewMode ? 'Switch to edit mode' : 'Preview markdown'}
- >
-
-
-
-
setWordWrap(!wordWrap)}
- className={cn(
- 'p-2 md:p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800',
- 'min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 flex items-center justify-center',
- wordWrap
- ? 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900'
- : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'
- )}
- title={wordWrap ? 'Disable word wrap' : 'Enable word wrap'}
- >
- ↵
-
-
-
setIsDarkMode(!isDarkMode)}
- className="p-2 md:p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 flex items-center justify-center"
- title="Toggle theme"
- >
- {isDarkMode ? '☀️' : '🌙'}
-
-
-
-
-
-
-
-
- Generate Tasks
-
-
-
- {saveSuccess ? (
- <>
-
-
-
- Saved!
- >
- ) : (
- <>
-
- {saving ? 'Saving...' : 'Save PRD'}
- >
- )}
-
-
-
- {isFullscreen ? : }
-
-
-
-
-
-
-
-
- {/* Editor/Preview Content */}
-
- {previewMode ? (
-
- ) : (
-
- )}
-
-
- {/* Footer */}
-
-
- Lines: {content.split('\n').length}
- Characters: {content.length}
- Words: {content.split(/\s+/).filter(word => word.length > 0).length}
- Format: Markdown
-
-
-
- Press Ctrl+S to save • Esc to close
-
-
-
-
- {/* Generate Tasks Modal */}
- {showGenerateModal && (
-
-
- {/* Header */}
-
-
-
-
-
-
Generate Tasks from PRD
-
-
setShowGenerateModal(false)}
- className="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700"
- >
-
-
-
-
- {/* Content */}
-
- {/* AI-First Approach */}
-
-
-
-
-
-
-
- 💡 Pro Tip: Ask Claude Code Directly!
-
-
- You can simply ask Claude Code in the chat to parse your PRD and generate tasks.
- The AI assistant will automatically save your PRD and create detailed tasks with implementation details.
-
-
-
-
💬 Example:
-
- "I've just initialized a new project with Claude Task Master. I have a PRD at .taskmaster/docs/{fileName.endsWith('.txt') || fileName.endsWith('.md') ? fileName : `${fileName}.txt`}. Can you help me parse it and set up the initial tasks?"
-
-
-
-
- This will: Save your PRD, analyze its content, and generate structured tasks with subtasks, dependencies, and implementation details.
-
-
-
-
-
- {/* Learn More Link */}
-
-
- {/* Footer */}
-
- setShowGenerateModal(false)}
- className="w-full px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors"
- >
- Got it, I'll ask Claude Code directly
-
-
-
-
-
- )}
-
- {/* Overwrite Confirmation Modal */}
- {showOverwriteConfirm && (
-
-
setShowOverwriteConfirm(false)} />
-
-
-
-
-
- File Already Exists
-
-
-
-
- A PRD file named "{fileName.endsWith('.txt') || fileName.endsWith('.md') ? fileName : `${fileName}.txt`}" already exists.
- Do you want to overwrite it with the current content?
-
-
-
- setShowOverwriteConfirm(false)}
- className="px-4 py-2 text-sm text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors"
- >
- Cancel
-
- {
- setShowOverwriteConfirm(false);
- await performSave();
- }}
- className="px-4 py-2 text-sm text-white bg-yellow-600 hover:bg-yellow-700 rounded-md flex items-center space-x-2 transition-colors"
- >
-
- Overwrite
-
-
-
-
-
- )}
-
- );
-};
-
-export default PRDEditor;
\ No newline at end of file
diff --git a/src/components/ProjectCreationWizard.jsx b/src/components/ProjectCreationWizard.jsx
deleted file mode 100644
index 5126d44a..00000000
--- a/src/components/ProjectCreationWizard.jsx
+++ /dev/null
@@ -1,875 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { X, FolderPlus, GitBranch, Key, ChevronRight, ChevronLeft, Check, Loader2, AlertCircle, FolderOpen, Eye, EyeOff, Plus } from 'lucide-react';
-import { Button } from './ui/button';
-import { Input } from './ui/input';
-import { api } from '../utils/api';
-import { useTranslation } from 'react-i18next';
-
-const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
- const { t } = useTranslation();
- // Wizard state
- const [step, setStep] = useState(1); // 1: Choose type, 2: Configure, 3: Confirm
- const [workspaceType, setWorkspaceType] = useState('existing'); // 'existing' or 'new' - default to 'existing'
-
- // Form state
- const [workspacePath, setWorkspacePath] = useState('');
- const [githubUrl, setGithubUrl] = useState('');
- const [selectedGithubToken, setSelectedGithubToken] = useState('');
- const [tokenMode, setTokenMode] = useState('stored'); // 'stored' | 'new' | 'none'
- const [newGithubToken, setNewGithubToken] = useState('');
-
- // UI state
- const [isCreating, setIsCreating] = useState(false);
- const [error, setError] = useState(null);
- const [availableTokens, setAvailableTokens] = useState([]);
- const [loadingTokens, setLoadingTokens] = useState(false);
- const [pathSuggestions, setPathSuggestions] = useState([]);
- const [showPathDropdown, setShowPathDropdown] = useState(false);
- const [showFolderBrowser, setShowFolderBrowser] = useState(false);
- const [browserCurrentPath, setBrowserCurrentPath] = useState('~');
- const [browserFolders, setBrowserFolders] = useState([]);
- const [loadingFolders, setLoadingFolders] = useState(false);
- const [showHiddenFolders, setShowHiddenFolders] = useState(false);
- const [showNewFolderInput, setShowNewFolderInput] = useState(false);
- const [newFolderName, setNewFolderName] = useState('');
- const [creatingFolder, setCreatingFolder] = useState(false);
- const [cloneProgress, setCloneProgress] = useState('');
-
- // Load available GitHub tokens when needed
- useEffect(() => {
- if (step === 2 && workspaceType === 'new' && githubUrl) {
- loadGithubTokens();
- }
- }, [step, workspaceType, githubUrl]);
-
- // Load path suggestions
- useEffect(() => {
- if (workspacePath.length > 2) {
- loadPathSuggestions(workspacePath);
- } else {
- setPathSuggestions([]);
- setShowPathDropdown(false);
- }
- }, [workspacePath]);
-
- const loadGithubTokens = async () => {
- try {
- setLoadingTokens(true);
- const response = await api.get('/settings/credentials?type=github_token');
- const data = await response.json();
-
- const activeTokens = (data.credentials || []).filter(t => t.is_active);
- setAvailableTokens(activeTokens);
-
- // Auto-select first token if available
- if (activeTokens.length > 0 && !selectedGithubToken) {
- setSelectedGithubToken(activeTokens[0].id.toString());
- }
- } catch (error) {
- console.error('Error loading GitHub tokens:', error);
- } finally {
- setLoadingTokens(false);
- }
- };
-
- const loadPathSuggestions = async (inputPath) => {
- try {
- // Extract the directory to browse (parent of input)
- const lastSlash = inputPath.lastIndexOf('/');
- const dirPath = lastSlash > 0 ? inputPath.substring(0, lastSlash) : '~';
-
- const response = await api.browseFilesystem(dirPath);
- const data = await response.json();
-
- if (data.suggestions) {
- // Filter suggestions based on the input, excluding exact match
- const filtered = data.suggestions.filter(s =>
- s.path.toLowerCase().startsWith(inputPath.toLowerCase()) &&
- s.path.toLowerCase() !== inputPath.toLowerCase()
- );
- setPathSuggestions(filtered.slice(0, 5));
- setShowPathDropdown(filtered.length > 0);
- }
- } catch (error) {
- console.error('Error loading path suggestions:', error);
- }
- };
-
- const handleNext = () => {
- setError(null);
-
- if (step === 1) {
- if (!workspaceType) {
- setError(t('projectWizard.errors.selectType'));
- return;
- }
- setStep(2);
- } else if (step === 2) {
- if (!workspacePath.trim()) {
- setError(t('projectWizard.errors.providePath'));
- return;
- }
-
- // No validation for GitHub token - it's optional (only needed for private repos)
- setStep(3);
- }
- };
-
- const handleBack = () => {
- setError(null);
- setStep(step - 1);
- };
-
- const handleCreate = async () => {
- setIsCreating(true);
- setError(null);
- setCloneProgress('');
-
- try {
- if (workspaceType === 'new' && githubUrl) {
- const params = new URLSearchParams({
- path: workspacePath.trim(),
- githubUrl: githubUrl.trim(),
- });
-
- if (tokenMode === 'stored' && selectedGithubToken) {
- params.append('githubTokenId', selectedGithubToken);
- } else if (tokenMode === 'new' && newGithubToken) {
- params.append('newGithubToken', newGithubToken.trim());
- }
-
- const token = localStorage.getItem('auth-token');
- const url = `/api/projects/clone-progress?${params}${token ? `&token=${token}` : ''}`;
-
- await new Promise((resolve, reject) => {
- const eventSource = new EventSource(url);
-
- eventSource.onmessage = (event) => {
- try {
- const data = JSON.parse(event.data);
-
- if (data.type === 'progress') {
- setCloneProgress(data.message);
- } else if (data.type === 'complete') {
- eventSource.close();
- if (onProjectCreated) {
- onProjectCreated(data.project);
- }
- onClose();
- resolve();
- } else if (data.type === 'error') {
- eventSource.close();
- reject(new Error(data.message));
- }
- } catch (e) {
- console.error('Error parsing SSE event:', e);
- }
- };
-
- eventSource.onerror = () => {
- eventSource.close();
- reject(new Error('Connection lost during clone'));
- };
- });
- return;
- }
-
- const payload = {
- workspaceType,
- path: workspacePath.trim(),
- };
-
- const response = await api.createWorkspace(payload);
- const data = await response.json();
-
- if (!response.ok) {
- throw new Error(data.details || data.error || t('projectWizard.errors.failedToCreate'));
- }
-
- if (onProjectCreated) {
- onProjectCreated(data.project);
- }
-
- onClose();
- } catch (error) {
- console.error('Error creating workspace:', error);
- setError(error.message || t('projectWizard.errors.failedToCreate'));
- } finally {
- setIsCreating(false);
- }
- };
-
- const selectPathSuggestion = (suggestion) => {
- setWorkspacePath(suggestion.path);
- setShowPathDropdown(false);
- };
-
- const openFolderBrowser = async () => {
- setShowFolderBrowser(true);
- await loadBrowserFolders('~');
- };
-
- const loadBrowserFolders = async (path) => {
- try {
- setLoadingFolders(true);
- const response = await api.browseFilesystem(path);
- const data = await response.json();
- setBrowserCurrentPath(data.path || path);
- setBrowserFolders(data.suggestions || []);
- } catch (error) {
- console.error('Error loading folders:', error);
- } finally {
- setLoadingFolders(false);
- }
- };
-
- const selectFolder = (folderPath, advanceToConfirm = false) => {
- setWorkspacePath(folderPath);
- setShowFolderBrowser(false);
- if (advanceToConfirm) {
- setStep(3);
- }
- };
-
- const navigateToFolder = async (folderPath) => {
- await loadBrowserFolders(folderPath);
- };
-
- const createNewFolder = async () => {
- if (!newFolderName.trim()) return;
- setCreatingFolder(true);
- setError(null);
- try {
- const separator = browserCurrentPath.includes('\\') ? '\\' : '/';
- const folderPath = `${browserCurrentPath}${separator}${newFolderName.trim()}`;
- const response = await api.createFolder(folderPath);
- const data = await response.json();
- if (!response.ok) {
- throw new Error(data.error || t('projectWizard.errors.failedToCreateFolder', 'Failed to create folder'));
- }
- setNewFolderName('');
- setShowNewFolderInput(false);
- await loadBrowserFolders(data.path || folderPath);
- } catch (error) {
- console.error('Error creating folder:', error);
- setError(error.message || t('projectWizard.errors.failedToCreateFolder', 'Failed to create folder'));
- } finally {
- setCreatingFolder(false);
- }
- };
-
- return (
-
-
- {/* Header */}
-
-
-
-
-
-
- {t('projectWizard.title')}
-
-
-
-
-
-
-
- {/* Progress Indicator */}
-
-
- {[1, 2, 3].map((s) => (
-
-
-
- {s < step ? : s}
-
-
- {s === 1 ? t('projectWizard.steps.type') : s === 2 ? t('projectWizard.steps.configure') : t('projectWizard.steps.confirm')}
-
-
- {s < 3 && (
-
- )}
-
- ))}
-
-
-
- {/* Content */}
-
- {/* Error Display */}
- {error && (
-
- )}
-
- {/* Step 1: Choose workspace type */}
- {step === 1 && (
-
-
-
- {t('projectWizard.step1.question')}
-
-
- {/* Existing Workspace */}
-
setWorkspaceType('existing')}
- className={`p-4 border-2 rounded-lg text-left transition-all ${
- workspaceType === 'existing'
- ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
- : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
- }`}
- >
-
-
-
-
-
-
- {t('projectWizard.step1.existing.title')}
-
-
- {t('projectWizard.step1.existing.description')}
-
-
-
-
-
- {/* New Workspace */}
-
setWorkspaceType('new')}
- className={`p-4 border-2 rounded-lg text-left transition-all ${
- workspaceType === 'new'
- ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
- : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
- }`}
- >
-
-
-
-
-
-
- {t('projectWizard.step1.new.title')}
-
-
- {t('projectWizard.step1.new.description')}
-
-
-
-
-
-
-
- )}
-
- {/* Step 2: Configure workspace */}
- {step === 2 && (
-
- {/* Workspace Path */}
-
-
- {workspaceType === 'existing' ? t('projectWizard.step2.existingPath') : t('projectWizard.step2.newPath')}
-
-
-
-
setWorkspacePath(e.target.value)}
- placeholder={workspaceType === 'existing' ? '/path/to/existing/workspace' : '/path/to/new/workspace'}
- className="w-full"
- />
- {showPathDropdown && pathSuggestions.length > 0 && (
-
- {pathSuggestions.map((suggestion, index) => (
-
selectPathSuggestion(suggestion)}
- className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
- >
- {suggestion.name}
- {suggestion.path}
-
- ))}
-
- )}
-
-
-
-
-
-
- {workspaceType === 'existing'
- ? t('projectWizard.step2.existingHelp')
- : t('projectWizard.step2.newHelp')}
-
-
-
- {/* GitHub URL (only for new workspace) */}
- {workspaceType === 'new' && (
- <>
-
-
- {t('projectWizard.step2.githubUrl')}
-
-
setGithubUrl(e.target.value)}
- placeholder="https://github.com/username/repository"
- className="w-full"
- />
-
- {t('projectWizard.step2.githubHelp')}
-
-
-
- {/* GitHub Token (only for HTTPS URLs - SSH uses SSH keys) */}
- {githubUrl && !githubUrl.startsWith('git@') && !githubUrl.startsWith('ssh://') && (
-
-
-
-
-
- {t('projectWizard.step2.githubAuth')}
-
-
- {t('projectWizard.step2.githubAuthHelp')}
-
-
-
-
- {loadingTokens ? (
-
-
- {t('projectWizard.step2.loadingTokens')}
-
- ) : availableTokens.length > 0 ? (
- <>
- {/* Token Selection Tabs */}
-
- setTokenMode('stored')}
- className={`px-3 py-2 text-sm font-medium rounded-lg transition-colors ${
- tokenMode === 'stored'
- ? 'bg-blue-500 text-white'
- : 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
- }`}
- >
- {t('projectWizard.step2.storedToken')}
-
- setTokenMode('new')}
- className={`px-3 py-2 text-sm font-medium rounded-lg transition-colors ${
- tokenMode === 'new'
- ? 'bg-blue-500 text-white'
- : 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
- }`}
- >
- {t('projectWizard.step2.newToken')}
-
- {
- setTokenMode('none');
- setSelectedGithubToken('');
- setNewGithubToken('');
- }}
- className={`px-3 py-2 text-sm font-medium rounded-lg transition-colors ${
- tokenMode === 'none'
- ? 'bg-green-500 text-white'
- : 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
- }`}
- >
- {t('projectWizard.step2.nonePublic')}
-
-
-
- {tokenMode === 'stored' ? (
-
-
- {t('projectWizard.step2.selectToken')}
-
- setSelectedGithubToken(e.target.value)}
- className="w-full px-3 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-sm"
- >
- {t('projectWizard.step2.selectTokenPlaceholder')}
- {availableTokens.map((token) => (
-
- {token.credential_name}
-
- ))}
-
-
- ) : tokenMode === 'new' ? (
-
-
- {t('projectWizard.step2.newToken')}
-
-
setNewGithubToken(e.target.value)}
- placeholder="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
- className="w-full"
- />
-
- {t('projectWizard.step2.tokenHelp')}
-
-
- ) : null}
- >
- ) : (
-
-
-
- {t('projectWizard.step2.publicRepoInfo')}
-
-
-
-
-
- {t('projectWizard.step2.optionalTokenPublic')}
-
-
setNewGithubToken(e.target.value)}
- placeholder={t('projectWizard.step2.tokenPublicPlaceholder')}
- className="w-full"
- />
-
- {t('projectWizard.step2.noTokensHelp')}
-
-
-
- )}
-
- )}
- >
- )}
-
- )}
-
- {/* Step 3: Confirm */}
- {step === 3 && (
-
-
-
- {t('projectWizard.step3.reviewConfig')}
-
-
-
- {t('projectWizard.step3.workspaceType')}
-
- {workspaceType === 'existing' ? t('projectWizard.step3.existingWorkspace') : t('projectWizard.step3.newWorkspace')}
-
-
-
- {t('projectWizard.step3.path')}
-
- {workspacePath}
-
-
- {workspaceType === 'new' && githubUrl && (
- <>
-
- {t('projectWizard.step3.cloneFrom')}
-
- {githubUrl}
-
-
-
- {t('projectWizard.step3.authentication')}
-
- {tokenMode === 'stored' && selectedGithubToken
- ? `${t('projectWizard.step3.usingStoredToken')} ${availableTokens.find(t => t.id.toString() === selectedGithubToken)?.credential_name || 'Unknown'}`
- : tokenMode === 'new' && newGithubToken
- ? t('projectWizard.step3.usingProvidedToken')
- : (githubUrl.startsWith('git@') || githubUrl.startsWith('ssh://'))
- ? t('projectWizard.step3.sshKey', 'SSH Key')
- : t('projectWizard.step3.noAuthentication')}
-
-
- >
- )}
-
-
-
-
- {isCreating && cloneProgress ? (
-
-
{t('projectWizard.step3.cloningRepository', 'Cloning repository...')}
-
- {cloneProgress}
-
-
- ) : (
-
- {workspaceType === 'existing'
- ? t('projectWizard.step3.existingInfo')
- : githubUrl
- ? t('projectWizard.step3.newWithClone')
- : t('projectWizard.step3.newEmpty')}
-
- )}
-
-
- )}
-
-
- {/* Footer */}
-
-
- {step === 1 ? (
- t('projectWizard.buttons.cancel')
- ) : (
- <>
-
- {t('projectWizard.buttons.back')}
- >
- )}
-
-
-
- {isCreating ? (
- <>
-
- {githubUrl ? t('projectWizard.buttons.cloning', 'Cloning...') : t('projectWizard.buttons.creating')}
- >
- ) : step === 3 ? (
- <>
-
- {t('projectWizard.buttons.createProject')}
- >
- ) : (
- <>
- {t('projectWizard.buttons.next')}
-
- >
- )}
-
-
-
-
- {/* Folder Browser Modal */}
- {showFolderBrowser && (
-
-
- {/* Browser Header */}
-
-
-
-
-
-
- Select Folder
-
-
-
-
setShowHiddenFolders(!showHiddenFolders)}
- className={`p-2 rounded-md transition-colors ${
- showHiddenFolders
- ? 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30'
- : 'text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
- }`}
- title={showHiddenFolders ? 'Hide hidden folders' : 'Show hidden folders'}
- >
- {showHiddenFolders ? : }
-
-
setShowNewFolderInput(!showNewFolderInput)}
- className={`p-2 rounded-md transition-colors ${
- showNewFolderInput
- ? 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30'
- : 'text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
- }`}
- title="Create new folder"
- >
-
-
-
setShowFolderBrowser(false)}
- className="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700"
- >
-
-
-
-
-
- {/* New Folder Input */}
- {showNewFolderInput && (
-
-
- setNewFolderName(e.target.value)}
- placeholder="New folder name"
- className="flex-1"
- onKeyDown={(e) => {
- if (e.key === 'Enter') createNewFolder();
- if (e.key === 'Escape') {
- setShowNewFolderInput(false);
- setNewFolderName('');
- }
- }}
- autoFocus
- />
-
- {creatingFolder ? : 'Create'}
-
- {
- setShowNewFolderInput(false);
- setNewFolderName('');
- }}
- >
- Cancel
-
-
-
- )}
-
- {/* Folder List */}
-
- {loadingFolders ? (
-
-
-
- ) : (
-
- {/* Parent Directory - check for Windows root (e.g., C:\) and Unix root */}
- {browserCurrentPath !== '~' && browserCurrentPath !== '/' && !/^[A-Za-z]:\\?$/.test(browserCurrentPath) && (
-
{
- const lastSlash = Math.max(browserCurrentPath.lastIndexOf('/'), browserCurrentPath.lastIndexOf('\\'));
- let parentPath;
- if (lastSlash <= 0) {
- parentPath = '/';
- } else if (lastSlash === 2 && /^[A-Za-z]:/.test(browserCurrentPath)) {
- parentPath = browserCurrentPath.substring(0, 3);
- } else {
- parentPath = browserCurrentPath.substring(0, lastSlash);
- }
- navigateToFolder(parentPath);
- }}
- className="w-full px-4 py-3 text-left hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg flex items-center gap-3"
- >
-
- ..
-
- )}
-
- {/* Folders */}
- {browserFolders.length === 0 ? (
-
- No subfolders found
-
- ) : (
- browserFolders
- .filter(folder => showHiddenFolders || !folder.name.startsWith('.'))
- .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))
- .map((folder, index) => (
-
- navigateToFolder(folder.path)}
- className="flex-1 px-4 py-3 text-left hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg flex items-center gap-3"
- >
-
- {folder.name}
-
- selectFolder(folder.path, workspaceType === 'existing')}
- className="text-xs px-3"
- >
- Select
-
-
- ))
- )}
-
- )}
-
-
- {/* Browser Footer with Current Path */}
-
-
- Path:
-
- {browserCurrentPath}
-
-
-
- {
- setShowFolderBrowser(false);
- setShowNewFolderInput(false);
- setNewFolderName('');
- }}
- >
- Cancel
-
- selectFolder(browserCurrentPath, workspaceType === 'existing')}
- >
- Use this folder
-
-
-
-
-
- )}
-
- );
-};
-
-export default ProjectCreationWizard;
diff --git a/src/components/ProtectedRoute.jsx b/src/components/ProtectedRoute.jsx
deleted file mode 100644
index 507efa87..00000000
--- a/src/components/ProtectedRoute.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import React from 'react';
-import { useAuth } from '../contexts/AuthContext';
-import SetupForm from './SetupForm';
-import LoginForm from './LoginForm';
-import Onboarding from './Onboarding';
-import { MessageSquare } from 'lucide-react';
-import { IS_PLATFORM } from '../constants/config';
-
-const LoadingScreen = () => (
-
-
-
-
Claude Code UI
-
-
Loading...
-
-
-);
-
-const ProtectedRoute = ({ children }) => {
- const { user, isLoading, needsSetup, hasCompletedOnboarding, refreshOnboardingStatus } = useAuth();
-
- if (IS_PLATFORM) {
- if (isLoading) {
- return
;
- }
-
- if (!hasCompletedOnboarding) {
- return
;
- }
-
- return children;
- }
-
- if (isLoading) {
- return
;
- }
-
- if (needsSetup) {
- return
;
- }
-
- if (!user) {
- return
;
- }
-
- if (!hasCompletedOnboarding) {
- return
;
- }
-
- return children;
-};
-
-export default ProtectedRoute;
\ No newline at end of file
diff --git a/src/components/QuickSettingsPanel.jsx b/src/components/QuickSettingsPanel.jsx
deleted file mode 100644
index fc69d492..00000000
--- a/src/components/QuickSettingsPanel.jsx
+++ /dev/null
@@ -1,448 +0,0 @@
-import React, { useState, useEffect, useRef, useCallback } from 'react';
-import {
- ChevronLeft,
- ChevronRight,
- Maximize2,
- Eye,
- Settings2,
- Moon,
- Sun,
- ArrowDown,
- Mic,
- Brain,
- Sparkles,
- FileText,
- Languages,
- GripVertical
-} from 'lucide-react';
-import { useTranslation } from 'react-i18next';
-import DarkModeToggle from './DarkModeToggle';
-
-import { useUiPreferences } from '../hooks/useUiPreferences';
-import { useTheme } from '../contexts/ThemeContext';
-import LanguageSelector from './LanguageSelector';
-
-import { useDeviceSettings } from '../hooks/useDeviceSettings';
-
-
-const QuickSettingsPanel = () => {
- const { t } = useTranslation('settings');
- const [isOpen, setIsOpen] = useState(false);
- const [whisperMode, setWhisperMode] = useState(() => {
- return localStorage.getItem('whisperMode') || 'default';
- });
- const { isDarkMode } = useTheme();
-
- const { isMobile } = useDeviceSettings({ trackPWA: false });
-
- const { preferences, setPreference } = useUiPreferences();
- const { autoExpandTools, showRawParameters, showThinking, autoScrollToBottom, sendByCtrlEnter } = preferences;
-
- // Draggable handle state
- const [handlePosition, setHandlePosition] = useState(() => {
- const saved = localStorage.getItem('quickSettingsHandlePosition');
- if (saved) {
- try {
- const parsed = JSON.parse(saved);
- return parsed.y ?? 50;
- } catch {
- // Remove corrupted data
- localStorage.removeItem('quickSettingsHandlePosition');
- return 50;
- }
- }
- return 50; // Default to 50% (middle of screen)
- });
-
- const [isDragging, setIsDragging] = useState(false);
- const [dragStartY, setDragStartY] = useState(0);
- const [dragStartPosition, setDragStartPosition] = useState(0);
- const [hasMoved, setHasMoved] = useState(false); // Track if user has moved during drag
- const handleRef = useRef(null);
- const constraintsRef = useRef({ min: 10, max: 90 }); // Percentage constraints
- const dragThreshold = 5; // Pixels to move before it's considered a drag
-
- // Save handle position to localStorage when it changes
- useEffect(() => {
- localStorage.setItem('quickSettingsHandlePosition', JSON.stringify({ y: handlePosition }));
- }, [handlePosition]);
-
- // Calculate position from percentage
- const getPositionStyle = useCallback(() => {
- if (isMobile) {
- // On mobile, convert percentage to pixels from bottom
- const bottomPixels = (window.innerHeight * handlePosition) / 100;
- return { bottom: `${bottomPixels}px` };
- } else {
- // On desktop, use top with percentage
- return { top: `${handlePosition}%`, transform: 'translateY(-50%)' };
- }
- }, [handlePosition, isMobile]);
-
- // Handle mouse/touch start
- const handleDragStart = useCallback((e) => {
- // Don't prevent default yet - we want to allow click if no drag happens
- e.stopPropagation();
-
- const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY;
- setDragStartY(clientY);
- setDragStartPosition(handlePosition);
- setHasMoved(false);
- setIsDragging(false); // Don't set dragging until threshold is passed
- }, [handlePosition]);
-
- // Handle mouse/touch move
- const handleDragMove = useCallback((e) => {
- if (dragStartY === 0) return; // Not in a potential drag
-
- const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY;
- const deltaY = Math.abs(clientY - dragStartY);
-
- // Check if we've moved past threshold
- if (!isDragging && deltaY > dragThreshold) {
- setIsDragging(true);
- setHasMoved(true);
- document.body.style.cursor = 'grabbing';
- document.body.style.userSelect = 'none';
-
- // Prevent body scroll on mobile during drag
- if (e.type.includes('touch')) {
- document.body.style.overflow = 'hidden';
- document.body.style.position = 'fixed';
- document.body.style.width = '100%';
- }
- }
-
- if (!isDragging) return;
-
- // Prevent scrolling on touch move
- if (e.type.includes('touch')) {
- e.preventDefault();
- }
-
- const actualDeltaY = clientY - dragStartY;
-
- // For top-based positioning (desktop), moving down increases top percentage
- // For bottom-based positioning (mobile), we need to invert
- let percentageDelta;
- if (isMobile) {
- // On mobile, moving down should decrease bottom position (increase percentage from top)
- percentageDelta = -(actualDeltaY / window.innerHeight) * 100;
- } else {
- // On desktop, moving down should increase top position
- percentageDelta = (actualDeltaY / window.innerHeight) * 100;
- }
-
- let newPosition = dragStartPosition + percentageDelta;
-
- // Apply constraints
- newPosition = Math.max(constraintsRef.current.min, Math.min(constraintsRef.current.max, newPosition));
-
- setHandlePosition(newPosition);
- }, [isDragging, dragStartY, dragStartPosition, isMobile, dragThreshold]);
-
- // Handle mouse/touch end
- const handleDragEnd = useCallback(() => {
- setIsDragging(false);
- setDragStartY(0);
- document.body.style.cursor = '';
- document.body.style.userSelect = '';
-
- // Restore body scroll on mobile
- document.body.style.overflow = '';
- document.body.style.position = '';
- document.body.style.width = '';
- }, []);
-
- // Cleanup body styles on unmount in case component unmounts while dragging
- useEffect(() => {
- return () => {
- document.body.style.cursor = '';
- document.body.style.userSelect = '';
- document.body.style.overflow = '';
- document.body.style.position = '';
- document.body.style.width = '';
- };
- }, []);
-
- // Set up global event listeners for drag
- useEffect(() => {
- if (dragStartY !== 0) {
- // Mouse events
- const handleMouseMove = (e) => handleDragMove(e);
- const handleMouseUp = () => handleDragEnd();
-
- // Touch events
- const handleTouchMove = (e) => handleDragMove(e);
- const handleTouchEnd = () => handleDragEnd();
-
- document.addEventListener('mousemove', handleMouseMove);
- document.addEventListener('mouseup', handleMouseUp);
- document.addEventListener('touchmove', handleTouchMove, { passive: false });
- document.addEventListener('touchend', handleTouchEnd);
-
- return () => {
- document.removeEventListener('mousemove', handleMouseMove);
- document.removeEventListener('mouseup', handleMouseUp);
- document.removeEventListener('touchmove', handleTouchMove);
- document.removeEventListener('touchend', handleTouchEnd);
- };
- }
- }, [dragStartY, handleDragMove, handleDragEnd]);
-
- const handleToggle = (e) => {
- // Don't toggle if user was dragging
- if (hasMoved) {
- e.preventDefault();
- setHasMoved(false);
- return;
- }
-
- setIsOpen((previous) => !previous);
- };
-
- return (
- <>
- {/* Pull Tab - Combined drag handle and toggle button */}
-
{
- // Start drag on mousedown
- handleDragStart(e);
- }}
- onTouchStart={(e) => {
- // Start drag on touchstart
- handleDragStart(e);
- }}
- className={`fixed ${
- isOpen ? 'right-64' : 'right-0'
- } z-50 ${isDragging ? '' : 'transition-all duration-150 ease-out'} bg-white dark:bg-gray-800 border ${
- isDragging ? 'border-blue-500 dark:border-blue-400' : 'border-gray-200 dark:border-gray-700'
- } rounded-l-md p-2 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors shadow-lg ${
- isDragging ? 'cursor-grabbing' : 'cursor-pointer'
- } touch-none`}
- style={{ ...getPositionStyle(), touchAction: 'none', WebkitTouchCallout: 'none', WebkitUserSelect: 'none' }}
- aria-label={isDragging ? t('quickSettings.dragHandle.dragging') : isOpen ? t('quickSettings.dragHandle.closePanel') : t('quickSettings.dragHandle.openPanel')}
- title={isDragging ? t('quickSettings.dragHandle.draggingStatus') : t('quickSettings.dragHandle.toggleAndMove')}
- >
- {isDragging ? (
-
- ) : isOpen ? (
-
- ) : (
-
- )}
-
-
- {/* Panel */}
-
-
- {/* Header */}
-
-
-
- {t('quickSettings.title')}
-
-
-
- {/* Settings Content */}
-
- {/* Appearance Settings */}
-
-
{t('quickSettings.sections.appearance')}
-
-
-
- {isDarkMode ? : }
- {t('quickSettings.darkMode')}
-
-
-
-
- {/* Language Selector */}
-
-
-
-
-
- {/* Tool Display Settings */}
-
-
{t('quickSettings.sections.toolDisplay')}
-
-
-
-
- {t('quickSettings.autoExpandTools')}
-
- setPreference('autoExpandTools', e.target.checked)}
- className="h-4 w-4 rounded border-gray-300 dark:border-gray-600 text-blue-600 dark:text-blue-500 focus:ring-blue-500 focus:ring-2 dark:focus:ring-blue-400 bg-gray-100 dark:bg-gray-800 checked:bg-blue-600 dark:checked:bg-blue-600"
- />
-
-
-
-
-
- {t('quickSettings.showRawParameters')}
-
- setPreference('showRawParameters', e.target.checked)}
- className="h-4 w-4 rounded border-gray-300 dark:border-gray-600 text-blue-600 dark:text-blue-500 focus:ring-blue-500 focus:ring-2 dark:focus:ring-blue-400 bg-gray-100 dark:bg-gray-800 checked:bg-blue-600 dark:checked:bg-blue-600"
- />
-
-
-
-
-
- {t('quickSettings.showThinking')}
-
- setPreference('showThinking', e.target.checked)}
- className="h-4 w-4 rounded border-gray-300 dark:border-gray-600 text-blue-600 dark:text-blue-500 focus:ring-blue-500 focus:ring-2 dark:focus:ring-blue-400 bg-gray-100 dark:bg-gray-800 checked:bg-blue-600 dark:checked:bg-blue-600"
- />
-
-
- {/* View Options */}
-
-
{t('quickSettings.sections.viewOptions')}
-
-
-
-
- {t('quickSettings.autoScrollToBottom')}
-
- setPreference('autoScrollToBottom', e.target.checked)}
- className="h-4 w-4 rounded border-gray-300 dark:border-gray-600 text-blue-600 dark:text-blue-500 focus:ring-blue-500 focus:ring-2 dark:focus:ring-blue-400 bg-gray-100 dark:bg-gray-800 checked:bg-blue-600 dark:checked:bg-blue-600"
- />
-
-
-
- {/* Input Settings */}
-
-
{t('quickSettings.sections.inputSettings')}
-
-
-
-
- {t('quickSettings.sendByCtrlEnter')}
-
- setPreference('sendByCtrlEnter', e.target.checked)}
- className="h-4 w-4 rounded border-gray-300 dark:border-gray-600 text-blue-600 dark:text-blue-500 focus:ring-blue-500 focus:ring-2 dark:focus:ring-blue-400 bg-gray-100 dark:bg-gray-800 checked:bg-blue-600 dark:checked:bg-blue-600"
- />
-
-
- {t('quickSettings.sendByCtrlEnterDescription')}
-
-
-
- {/* Whisper Dictation Settings - HIDDEN */}
-
-
{t('quickSettings.sections.whisperDictation')}
-
-
-
- {
- setWhisperMode('default');
- localStorage.setItem('whisperMode', 'default');
- window.dispatchEvent(new Event('whisperModeChanged'));
- }}
- className="mt-0.5 h-4 w-4 border-gray-300 dark:border-gray-600 text-blue-600 dark:text-blue-500 focus:ring-blue-500 dark:focus:ring-blue-400 dark:bg-gray-800 dark:checked:bg-blue-600"
- />
-
-
-
- {t('quickSettings.whisper.modes.default')}
-
-
- {t('quickSettings.whisper.modes.defaultDescription')}
-
-
-
-
-
- {
- setWhisperMode('prompt');
- localStorage.setItem('whisperMode', 'prompt');
- window.dispatchEvent(new Event('whisperModeChanged'));
- }}
- className="mt-0.5 h-4 w-4 border-gray-300 dark:border-gray-600 text-blue-600 dark:text-blue-500 focus:ring-blue-500 dark:focus:ring-blue-400 dark:bg-gray-800 dark:checked:bg-blue-600"
- />
-
-
-
- {t('quickSettings.whisper.modes.prompt')}
-
-
- {t('quickSettings.whisper.modes.promptDescription')}
-
-
-
-
-
- {
- setWhisperMode('vibe');
- localStorage.setItem('whisperMode', 'vibe');
- window.dispatchEvent(new Event('whisperModeChanged'));
- }}
- className="mt-0.5 h-4 w-4 border-gray-300 dark:border-gray-600 text-blue-600 dark:text-blue-500 focus:ring-blue-500 dark:focus:ring-blue-400 dark:bg-gray-800 dark:checked:bg-blue-600"
- />
-
-
-
- {t('quickSettings.whisper.modes.vibe')}
-
-
- {t('quickSettings.whisper.modes.vibeDescription')}
-
-
-
-
-
-
-
-
-
- {/* Backdrop */}
- {isOpen && (
-
- )}
- >
- );
-};
-
-export default QuickSettingsPanel;
\ No newline at end of file
diff --git a/src/components/SetupForm.jsx b/src/components/SetupForm.jsx
deleted file mode 100644
index 4f41d0b7..00000000
--- a/src/components/SetupForm.jsx
+++ /dev/null
@@ -1,133 +0,0 @@
-import React, { useState } from 'react';
-import { useAuth } from '../contexts/AuthContext';
-const SetupForm = () => {
- const [username, setUsername] = useState('');
- const [password, setPassword] = useState('');
- const [confirmPassword, setConfirmPassword] = useState('');
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState('');
-
- const { register } = useAuth();
-
- const handleSubmit = async (e) => {
- e.preventDefault();
- setError('');
-
- if (password !== confirmPassword) {
- setError('Passwords do not match');
- return;
- }
-
- if (username.length < 3) {
- setError('Username must be at least 3 characters long');
- return;
- }
-
- if (password.length < 6) {
- setError('Password must be at least 6 characters long');
- return;
- }
-
- setIsLoading(true);
-
- const result = await register(username, password);
-
- if (!result.success) {
- setError(result.error);
- }
-
- setIsLoading(false);
- };
-
- return (
-
-
-
- {/* Logo and Title */}
-
-
-
-
-
Welcome to Claude Code UI
-
- Set up your account to get started
-
-
-
- {/* Setup Form */}
-
-
-
- Username
-
- setUsername(e.target.value)}
- className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
- placeholder="Enter your username"
- required
- disabled={isLoading}
- />
-
-
-
-
- Password
-
- setPassword(e.target.value)}
- className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
- placeholder="Enter your password"
- required
- disabled={isLoading}
- />
-
-
-
-
- Confirm Password
-
- setConfirmPassword(e.target.value)}
- className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
- placeholder="Confirm your password"
- required
- disabled={isLoading}
- />
-
-
- {error && (
-
- )}
-
-
- {isLoading ? 'Setting up...' : 'Create Account'}
-
-
-
-
-
- This is a single-user system. Only one account can be created.
-
-
-
-
-
- );
-};
-
-export default SetupForm;
\ No newline at end of file
diff --git a/src/components/TaskCard.jsx b/src/components/TaskCard.jsx
deleted file mode 100644
index 3b363a4c..00000000
--- a/src/components/TaskCard.jsx
+++ /dev/null
@@ -1,210 +0,0 @@
-import React from 'react';
-import { Clock, CheckCircle, Circle, AlertCircle, Pause, X, ArrowRight, ChevronUp, Minus, Flag } from 'lucide-react';
-import { cn } from '../lib/utils';
-import Tooltip from './Tooltip';
-
-const TaskCard = ({
- task,
- onClick,
- showParent = false,
- className = ''
-}) => {
- const getStatusConfig = (status) => {
- switch (status) {
- case 'done':
- return {
- icon: CheckCircle,
- bgColor: 'bg-green-50 dark:bg-green-950',
- borderColor: 'border-green-200 dark:border-green-800',
- iconColor: 'text-green-600 dark:text-green-400',
- textColor: 'text-green-900 dark:text-green-100',
- statusText: 'Done'
- };
-
- case 'in-progress':
- return {
- icon: Clock,
- bgColor: 'bg-blue-50 dark:bg-blue-950',
- borderColor: 'border-blue-200 dark:border-blue-800',
- iconColor: 'text-blue-600 dark:text-blue-400',
- textColor: 'text-blue-900 dark:text-blue-100',
- statusText: 'In Progress'
- };
-
- case 'review':
- return {
- icon: AlertCircle,
- bgColor: 'bg-amber-50 dark:bg-amber-950',
- borderColor: 'border-amber-200 dark:border-amber-800',
- iconColor: 'text-amber-600 dark:text-amber-400',
- textColor: 'text-amber-900 dark:text-amber-100',
- statusText: 'Review'
- };
-
- case 'deferred':
- return {
- icon: Pause,
- bgColor: 'bg-gray-50 dark:bg-gray-800',
- borderColor: 'border-gray-200 dark:border-gray-700',
- iconColor: 'text-gray-500 dark:text-gray-400',
- textColor: 'text-gray-700 dark:text-gray-300',
- statusText: 'Deferred'
- };
-
- case 'cancelled':
- return {
- icon: X,
- bgColor: 'bg-red-50 dark:bg-red-950',
- borderColor: 'border-red-200 dark:border-red-800',
- iconColor: 'text-red-600 dark:text-red-400',
- textColor: 'text-red-900 dark:text-red-100',
- statusText: 'Cancelled'
- };
-
- case 'pending':
- default:
- return {
- icon: Circle,
- bgColor: 'bg-slate-50 dark:bg-slate-800',
- borderColor: 'border-slate-200 dark:border-slate-700',
- iconColor: 'text-slate-500 dark:text-slate-400',
- textColor: 'text-slate-900 dark:text-slate-100',
- statusText: 'Pending'
- };
- }
- };
-
- const config = getStatusConfig(task.status);
- const Icon = config.icon;
-
- const getPriorityIcon = (priority) => {
- switch (priority) {
- case 'high':
- return (
-
-
-
-
-
- );
- case 'medium':
- return (
-
-
-
-
-
- );
- case 'low':
- return (
-
-
-
-
-
- );
- default:
- return (
-
-
-
-
-
- );
- }
- };
-
- return (
-
- {/* Header with Task ID, Title, and Priority */}
-
- {/* Task ID and Title */}
-
-
-
-
- {task.id}
-
-
-
-
- {task.title}
-
- {showParent && task.parentId && (
-
- Task {task.parentId}
-
- )}
-
-
- {/* Priority Icon */}
-
- {getPriorityIcon(task.priority)}
-
-
-
- {/* Footer with Dependencies and Status */}
-
- {/* Dependencies */}
-
- {task.dependencies && Array.isArray(task.dependencies) && task.dependencies.length > 0 && (
-
`Task ${dep}`).join(', ')}`}>
-
-
-
Depends on: {task.dependencies.join(', ')}
-
-
- )}
-
-
- {/* Status Badge */}
-
-
-
-
- {config.statusText}
-
-
-
-
-
- {/* Subtask Progress (if applicable) */}
- {task.subtasks && task.subtasks.length > 0 && (
-
-
-
Progress:
-
st.status === 'done').length} of ${task.subtasks.length} subtasks completed`}>
-
-
st.status === 'done').length / task.subtasks.length) * 100)}%`
- }}
- />
-
-
-
st.status === 'done').length} completed, ${task.subtasks.filter(st => st.status === 'pending').length} pending, ${task.subtasks.filter(st => st.status === 'in-progress').length} in progress`}>
-
- {task.subtasks.filter(st => st.status === 'done').length}/{task.subtasks.length}
-
-
-
-
- )}
-
- );
-};
-
-export default TaskCard;
\ No newline at end of file
diff --git a/src/components/TaskDetail.jsx b/src/components/TaskDetail.jsx
deleted file mode 100644
index 4d881869..00000000
--- a/src/components/TaskDetail.jsx
+++ /dev/null
@@ -1,407 +0,0 @@
-import React, { useState } from 'react';
-import { X, Flag, User, ArrowRight, CheckCircle, Circle, AlertCircle, Pause, Edit, Save, Copy, ChevronDown, ChevronRight, Clock } from 'lucide-react';
-import { cn } from '../lib/utils';
-import TaskIndicator from './TaskIndicator';
-import { api } from '../utils/api';
-import { useTaskMaster } from '../contexts/TaskMasterContext';
-import { copyTextToClipboard } from '../utils/clipboard';
-
-const TaskDetail = ({
- task,
- onClose,
- onEdit,
- onStatusChange,
- onTaskClick,
- isOpen = true,
- className = ''
-}) => {
- const [editMode, setEditMode] = useState(false);
- const [editedTask, setEditedTask] = useState(task || {});
- const [isSaving, setIsSaving] = useState(false);
- const [showDetails, setShowDetails] = useState(false);
- const [showTestStrategy, setShowTestStrategy] = useState(false);
- const { currentProject, refreshTasks } = useTaskMaster();
-
- if (!isOpen || !task) return null;
-
- const handleSave = async () => {
- if (!currentProject) return;
-
- setIsSaving(true);
- try {
- // Only include changed fields
- const updates = {};
- if (editedTask.title !== task.title) updates.title = editedTask.title;
- if (editedTask.description !== task.description) updates.description = editedTask.description;
- if (editedTask.details !== task.details) updates.details = editedTask.details;
-
- if (Object.keys(updates).length > 0) {
- const response = await api.taskmaster.updateTask(currentProject.name, task.id, updates);
-
- if (response.ok) {
- // Refresh tasks to get updated data
- refreshTasks?.();
- onEdit?.(editedTask);
- setEditMode(false);
- } else {
- const error = await response.json();
- console.error('Failed to update task:', error);
- alert(`Failed to update task: ${error.message}`);
- }
- } else {
- setEditMode(false);
- }
- } catch (error) {
- console.error('Error updating task:', error);
- alert('Error updating task. Please try again.');
- } finally {
- setIsSaving(false);
- }
- };
-
- const handleStatusChange = async (newStatus) => {
- if (!currentProject) return;
-
- try {
- const response = await api.taskmaster.updateTask(currentProject.name, task.id, { status: newStatus });
-
- if (response.ok) {
- refreshTasks?.();
- onStatusChange?.(task.id, newStatus);
- } else {
- const error = await response.json();
- console.error('Failed to update task status:', error);
- alert(`Failed to update task status: ${error.message}`);
- }
- } catch (error) {
- console.error('Error updating task status:', error);
- alert('Error updating task status. Please try again.');
- }
- };
-
- const copyTaskId = () => {
- copyTextToClipboard(task.id.toString());
- };
-
- const getStatusConfig = (status) => {
- switch (status) {
- case 'done':
- return { icon: CheckCircle, color: 'text-green-600 dark:text-green-400', bg: 'bg-green-50 dark:bg-green-950' };
- case 'in-progress':
- return { icon: Clock, color: 'text-blue-600 dark:text-blue-400', bg: 'bg-blue-50 dark:bg-blue-950' };
- case 'review':
- return { icon: AlertCircle, color: 'text-amber-600 dark:text-amber-400', bg: 'bg-amber-50 dark:bg-amber-950' };
- case 'deferred':
- return { icon: Pause, color: 'text-gray-500 dark:text-gray-400', bg: 'bg-gray-50 dark:bg-gray-800' };
- case 'cancelled':
- return { icon: X, color: 'text-red-600 dark:text-red-400', bg: 'bg-red-50 dark:bg-red-950' };
- default:
- return { icon: Circle, color: 'text-slate-500 dark:text-slate-400', bg: 'bg-slate-50 dark:bg-slate-800' };
- }
- };
-
- const statusConfig = getStatusConfig(task.status);
- const StatusIcon = statusConfig.icon;
-
-
- const getPriorityColor = (priority) => {
- switch (priority) {
- case 'high': return 'text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-950';
- case 'medium': return 'text-yellow-600 dark:text-yellow-400 bg-yellow-50 dark:bg-yellow-950';
- case 'low': return 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-950';
- default: return 'text-gray-600 dark:text-gray-400 bg-gray-50 dark:bg-gray-800';
- }
- };
-
- const statusOptions = [
- { value: 'pending', label: 'Pending' },
- { value: 'in-progress', label: 'In Progress' },
- { value: 'review', label: 'Review' },
- { value: 'done', label: 'Done' },
- { value: 'deferred', label: 'Deferred' },
- { value: 'cancelled', label: 'Cancelled' }
- ];
-
- return (
-
-
- {/* Header */}
-
-
-
-
-
-
- Task {task.id}
-
-
- {task.parentId && (
-
- Subtask of Task {task.parentId}
-
- )}
-
- {editMode ? (
-
setEditedTask({ ...editedTask, title: e.target.value })}
- className="w-full text-lg font-semibold bg-transparent border-b-2 border-blue-500 focus:outline-none text-gray-900 dark:text-white"
- placeholder="Task title"
- />
- ) : (
-
- {task.title}
-
- )}
-
-
-
-
- {editMode ? (
- <>
-
-
-
- {
- setEditMode(false);
- setEditedTask(task);
- }}
- disabled={isSaving}
- className="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
- title="Cancel editing"
- >
-
-
- >
- ) : (
- setEditMode(true)}
- className="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-md transition-colors"
- title="Edit task"
- >
-
-
- )}
-
-
-
-
-
-
-
- {/* Content */}
-
- {/* Status and Metadata Row */}
-
- {/* Status */}
-
-
Status
-
-
-
-
- {statusOptions.find(option => option.value === task.status)?.label || task.status}
-
-
-
-
-
- {/* Priority */}
-
-
Priority
-
-
- {task.priority || 'Not set'}
-
-
-
- {/* Dependencies */}
-
-
Dependencies
- {task.dependencies && task.dependencies.length > 0 ? (
-
- {task.dependencies.map(depId => (
-
onTaskClick && onTaskClick({ id: depId })}
- className="px-2 py-1 bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 rounded text-sm hover:bg-blue-200 dark:hover:bg-blue-800 transition-colors cursor-pointer disabled:cursor-default disabled:opacity-50"
- disabled={!onTaskClick}
- title={onTaskClick ? `Click to view Task ${depId}` : `Task ${depId}`}
- >
-
- {depId}
-
- ))}
-
- ) : (
-
No dependencies
- )}
-
-
-
- {/* Description */}
-
-
Description
- {editMode ? (
-
setEditedTask({ ...editedTask, description: e.target.value })}
- rows={3}
- className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
- placeholder="Task description"
- />
- ) : (
-
- {task.description || 'No description provided'}
-
- )}
-
-
- {/* Implementation Details */}
- {task.details && (
-
-
setShowDetails(!showDetails)}
- className="w-full flex items-center justify-between p-4 text-left hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
- >
-
- Implementation Details
-
- {showDetails ? (
-
- ) : (
-
- )}
-
- {showDetails && (
-
- {editMode ? (
-
setEditedTask({ ...editedTask, details: e.target.value })}
- rows={4}
- className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
- placeholder="Implementation details"
- />
- ) : (
-
- )}
-
- )}
-
- )}
-
- {/* Test Strategy */}
- {task.testStrategy && (
-
-
setShowTestStrategy(!showTestStrategy)}
- className="w-full flex items-center justify-between p-4 text-left hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
- >
-
- Test Strategy
-
- {showTestStrategy ? (
-
- ) : (
-
- )}
-
- {showTestStrategy && (
-
-
-
- {task.testStrategy}
-
-
-
- )}
-
- )}
-
- {/* Subtasks */}
- {task.subtasks && task.subtasks.length > 0 && (
-
-
- Subtasks ({task.subtasks.length})
-
-
- {task.subtasks.map(subtask => {
- const subtaskConfig = getStatusConfig(subtask.status);
- const SubtaskIcon = subtaskConfig.icon;
- return (
-
-
-
-
- {subtask.title}
-
- {subtask.description && (
-
- {subtask.description}
-
- )}
-
-
- {subtask.id}
-
-
- );
- })}
-
-
- )}
-
-
-
- {/* Footer */}
-
-
- Task ID: {task.id}
-
-
-
-
- Close
-
-
-
-
-
- );
-};
-
-export default TaskDetail;
\ No newline at end of file
diff --git a/src/components/TaskIndicator.jsx b/src/components/TaskIndicator.jsx
deleted file mode 100644
index 9b12b48a..00000000
--- a/src/components/TaskIndicator.jsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import React from 'react';
-import { CheckCircle, Settings, X, AlertCircle } from 'lucide-react';
-import { cn } from '../lib/utils';
-
-/**
- * TaskIndicator Component
- *
- * Displays TaskMaster status for projects in the sidebar with appropriate
- * icons and colors based on the project's TaskMaster configuration state.
- */
-const TaskIndicator = ({
- status = 'not-configured',
- size = 'sm',
- className = '',
- showLabel = false
-}) => {
- const getIndicatorConfig = () => {
- switch (status) {
- case 'fully-configured':
- return {
- icon: CheckCircle,
- color: 'text-green-500 dark:text-green-400',
- bgColor: 'bg-green-50 dark:bg-green-950',
- label: 'TaskMaster Ready',
- title: 'TaskMaster fully configured with MCP server'
- };
-
- case 'taskmaster-only':
- return {
- icon: Settings,
- color: 'text-blue-500 dark:text-blue-400',
- bgColor: 'bg-blue-50 dark:bg-blue-950',
- label: 'TaskMaster Init',
- title: 'TaskMaster initialized, MCP server needs setup'
- };
-
- case 'mcp-only':
- return {
- icon: AlertCircle,
- color: 'text-amber-500 dark:text-amber-400',
- bgColor: 'bg-amber-50 dark:bg-amber-950',
- label: 'MCP Ready',
- title: 'MCP server configured, TaskMaster needs initialization'
- };
-
- case 'not-configured':
- case 'error':
- default:
- return {
- icon: X,
- color: 'text-gray-400 dark:text-gray-500',
- bgColor: 'bg-gray-50 dark:bg-gray-900',
- label: 'No TaskMaster',
- title: 'TaskMaster not configured'
- };
- }
- };
-
- const config = getIndicatorConfig();
- const Icon = config.icon;
-
- const sizeClasses = {
- xs: 'w-3 h-3',
- sm: 'w-4 h-4',
- md: 'w-5 h-5',
- lg: 'w-6 h-6'
- };
-
- const paddingClasses = {
- xs: 'p-0.5',
- sm: 'p-1',
- md: 'p-1.5',
- lg: 'p-2'
- };
-
- if (showLabel) {
- return (
-
-
- {config.label}
-
- );
- }
-
- return (
-
-
-
- );
-};
-
-export default TaskIndicator;
\ No newline at end of file
diff --git a/src/components/TaskList.jsx b/src/components/TaskList.jsx
deleted file mode 100644
index 1e89310f..00000000
--- a/src/components/TaskList.jsx
+++ /dev/null
@@ -1,1055 +0,0 @@
-import React, { useState, useMemo, useEffect, useRef } from 'react';
-import { Search, Filter, ArrowUpDown, ArrowUp, ArrowDown, List, Grid, ChevronDown, Columns, Plus, Settings, Terminal, FileText, HelpCircle, X } from 'lucide-react';
-import { cn } from '../lib/utils';
-import TaskCard from './TaskCard';
-import CreateTaskModal from './CreateTaskModal';
-import { useTaskMaster } from '../contexts/TaskMasterContext';
-import Shell from './shell/view/Shell';
-import { api } from '../utils/api';
-import { useTranslation } from 'react-i18next';
-
-const TaskList = ({
- tasks = [],
- onTaskClick,
- className = '',
- showParentTasks = false,
- defaultView = 'kanban', // 'list', 'grid', or 'kanban'
- currentProject,
- onTaskCreated,
- onShowPRDEditor,
- existingPRDs = [],
- onRefreshPRDs
-}) => {
- const [searchTerm, setSearchTerm] = useState('');
- const [statusFilter, setStatusFilter] = useState('all');
- const [priorityFilter, setPriorityFilter] = useState('all');
- const [sortBy, setSortBy] = useState('id'); // 'id', 'title', 'status', 'priority', 'updated'
- const [sortOrder, setSortOrder] = useState('asc'); // 'asc' or 'desc'
- const [viewMode, setViewMode] = useState(defaultView);
- const [showFilters, setShowFilters] = useState(false);
- const [showCreateModal, setShowCreateModal] = useState(false);
- const [showCLI, setShowCLI] = useState(false);
- const [showHelpGuide, setShowHelpGuide] = useState(false);
- const [isTaskMasterComplete, setIsTaskMasterComplete] = useState(false);
- const [showPRDDropdown, setShowPRDDropdown] = useState(false);
- const dropdownRef = useRef(null);
-
- const { projectTaskMaster, refreshProjects, refreshTasks, setCurrentProject } = useTaskMaster();
- const { t } = useTranslation('tasks');
-
- // Close PRD dropdown when clicking outside
- useEffect(() => {
- const handleClickOutside = (event) => {
- if (
- showPRDDropdown &&
- dropdownRef.current &&
- !dropdownRef.current.contains(event.target)
- ) {
- setShowPRDDropdown(false);
- }
- };
-
- document.addEventListener('mousedown', handleClickOutside);
- return () => document.removeEventListener('mousedown', handleClickOutside);
- }, [showPRDDropdown]);
-
- const loadPRDOptions = async (prd, closeDropdown = false) => {
- if (!currentProject) {
- return;
- }
-
- try {
- const response = await api.get(`/taskmaster/prd/${encodeURIComponent(currentProject.name)}/${encodeURIComponent(prd.name)}`);
- if (response.ok) {
- const prdData = await response.json();
- onShowPRDEditor?.({
- name: prd.name,
- content: prdData.content,
- isExisting: true
- });
- if (closeDropdown) {
- setShowPRDDropdown(false);
- }
- } else {
- console.error('Failed to load PRD:', response.statusText);
- }
- } catch (error) {
- console.error('Error loading PRD:', error);
- }
- };
-
- // Get unique status values from tasks
- const statuses = useMemo(() => {
- const statusSet = new Set(tasks.map(task => task.status).filter(Boolean));
- return Array.from(statusSet).sort();
- }, [tasks]);
-
- // Get unique priority values from tasks
- const priorities = useMemo(() => {
- const prioritySet = new Set(tasks.map(task => task.priority).filter(Boolean));
- return Array.from(prioritySet).sort();
- }, [tasks]);
-
- // Filter and sort tasks
- const filteredAndSortedTasks = useMemo(() => {
- let filtered = tasks.filter(task => {
- // Text search
- const searchLower = searchTerm.toLowerCase();
- const matchesSearch = !searchTerm ||
- task.title.toLowerCase().includes(searchLower) ||
- task.description?.toLowerCase().includes(searchLower) ||
- task.id.toString().includes(searchLower);
-
- // Status filter
- const matchesStatus = statusFilter === 'all' || task.status === statusFilter;
-
- // Priority filter
- const matchesPriority = priorityFilter === 'all' || task.priority === priorityFilter;
-
- return matchesSearch && matchesStatus && matchesPriority;
- });
-
- // Sort tasks
- filtered.sort((a, b) => {
- let aVal, bVal;
-
- switch (sortBy) {
- case 'title':
- aVal = a.title.toLowerCase();
- bVal = b.title.toLowerCase();
- break;
- case 'status':
- // Custom status ordering: pending, in-progress, done, blocked, deferred, cancelled
- const statusOrder = { pending: 1, 'in-progress': 2, done: 3, blocked: 4, deferred: 5, cancelled: 6 };
- aVal = statusOrder[a.status] || 99;
- bVal = statusOrder[b.status] || 99;
- break;
- case 'priority':
- // Custom priority ordering: high should be sorted first in descending
- const priorityOrder = { high: 3, medium: 2, low: 1 };
- aVal = priorityOrder[a.priority] || 0;
- bVal = priorityOrder[b.priority] || 0;
- break;
- case 'updated':
- aVal = new Date(a.updatedAt || a.createdAt || 0);
- bVal = new Date(b.updatedAt || b.createdAt || 0);
- break;
- case 'id':
- default:
- // Handle numeric and dotted IDs (1, 1.1, 1.2, 2, 2.1, etc.)
- const parseId = (id) => {
- const parts = id.toString().split('.');
- return parts.map(part => parseInt(part, 10));
- };
-
- const aIds = parseId(a.id);
- const bIds = parseId(b.id);
-
- // Compare each part
- for (let i = 0; i < Math.max(aIds.length, bIds.length); i++) {
- const aId = aIds[i] || 0;
- const bId = bIds[i] || 0;
- if (aId !== bId) {
- aVal = aId;
- bVal = bId;
- break;
- }
- }
- break;
- }
-
- if (sortBy === 'updated') {
- return sortOrder === 'asc' ? aVal - bVal : bVal - aVal;
- }
-
- if (typeof aVal === 'string') {
- return sortOrder === 'asc' ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
- }
-
- return sortOrder === 'asc' ? aVal - bVal : bVal - aVal;
- });
-
- return filtered;
- }, [tasks, searchTerm, statusFilter, priorityFilter, sortBy, sortOrder]);
-
- // Organize tasks by status for Kanban view
- const kanbanColumns = useMemo(() => {
- const allColumns = [
- {
- id: 'pending',
- title: t('kanban.pending'),
- status: 'pending',
- color: 'bg-slate-50 dark:bg-slate-900/50 border-slate-200 dark:border-slate-700',
- headerColor: 'bg-slate-100 dark:bg-slate-800 text-slate-800 dark:text-slate-200'
- },
- {
- id: 'in-progress',
- title: t('kanban.inProgress'),
- status: 'in-progress',
- color: 'bg-blue-50 dark:bg-blue-900/50 border-blue-200 dark:border-blue-700',
- headerColor: 'bg-blue-100 dark:bg-blue-800 text-blue-800 dark:text-blue-200'
- },
- {
- id: 'done',
- title: t('kanban.done'),
- status: 'done',
- color: 'bg-emerald-50 dark:bg-emerald-900/50 border-emerald-200 dark:border-emerald-700',
- headerColor: 'bg-emerald-100 dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200'
- },
- {
- id: 'blocked',
- title: t('kanban.blocked'),
- status: 'blocked',
- color: 'bg-red-50 dark:bg-red-900/50 border-red-200 dark:border-red-700',
- headerColor: 'bg-red-100 dark:bg-red-800 text-red-800 dark:text-red-200'
- },
- {
- id: 'deferred',
- title: t('kanban.deferred'),
- status: 'deferred',
- color: 'bg-amber-50 dark:bg-amber-900/50 border-amber-200 dark:border-amber-700',
- headerColor: 'bg-amber-100 dark:bg-amber-800 text-amber-800 dark:text-amber-200'
- },
- {
- id: 'cancelled',
- title: t('kanban.cancelled'),
- status: 'cancelled',
- color: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700',
- headerColor: 'bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200'
- }
- ];
-
- // Only show columns that have tasks or are part of the main workflow
- const mainWorkflowStatuses = ['pending', 'in-progress', 'done'];
- const columnsWithTasks = allColumns.filter(column => {
- const hasTask = filteredAndSortedTasks.some(task => task.status === column.status);
- const isMainWorkflow = mainWorkflowStatuses.includes(column.status);
- return hasTask || isMainWorkflow;
- });
-
- return columnsWithTasks.map(column => ({
- ...column,
- tasks: filteredAndSortedTasks.filter(task => task.status === column.status)
- }));
- }, [filteredAndSortedTasks, t]);
-
- const handleSortChange = (newSortBy) => {
- if (sortBy === newSortBy) {
- setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
- } else {
- setSortBy(newSortBy);
- setSortOrder('asc');
- }
- };
-
- const clearFilters = () => {
- setSearchTerm('');
- setStatusFilter('all');
- setPriorityFilter('all');
- };
-
- const getSortIcon = (field) => {
- if (sortBy !== field) return
;
- return sortOrder === 'asc' ?
:
;
- };
-
- if (tasks.length === 0) {
- // Check if TaskMaster is configured by looking for .taskmaster directory
- const hasTaskMasterDirectory = currentProject?.taskMasterConfigured ||
- currentProject?.taskmaster?.hasTaskmaster ||
- projectTaskMaster?.hasTaskmaster;
-
- return (
-
- {!hasTaskMasterDirectory ? (
- // TaskMaster not configured
-
-
-
-
-
- {t('notConfigured.title')}
-
-
- {t('notConfigured.description')}
-
-
- {/* What is TaskMaster section */}
-
-
- {t('notConfigured.whatIsTitle')}
-
-
-
• {t('notConfigured.features.aiPowered')}
-
• {t('notConfigured.features.prdTemplates')}
-
• {t('notConfigured.features.dependencyTracking')}
-
• {t('notConfigured.features.progressVisualization')}
-
• {t('notConfigured.features.cliIntegration')}
-
-
-
-
{
- setIsTaskMasterComplete(false); // Reset completion state
- setShowCLI(true);
- }}
- className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors flex items-center gap-2 mx-auto"
- >
-
- {t('notConfigured.initializeButton')}
-
-
- ) : (
- // TaskMaster configured but no tasks - show Getting Started guide
-
-
-
-
-
-
-
-
{t('gettingStarted.title')}
-
{t('gettingStarted.subtitle')}
-
-
-
-
-
- {/* Step 1 */}
-
-
1
-
-
{t('gettingStarted.steps.createPRD.title')}
-
{t('gettingStarted.steps.createPRD.description')}
-
{
- onShowPRDEditor?.();
- }}
- className="inline-flex items-center gap-1 text-xs bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 px-2 py-1 rounded hover:bg-purple-200 dark:hover:bg-purple-900/50 transition-colors"
- >
-
- {t('gettingStarted.steps.createPRD.addButton')}
-
-
- {/* Show existing PRDs if any */}
- {existingPRDs.length > 0 && (
-
-
{t('gettingStarted.steps.createPRD.existingPRDs')}
-
- {existingPRDs.map((prd) => (
- {
- void loadPRDOptions(prd);
- }}
- className="inline-flex items-center gap-1 text-xs bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-2 py-1 rounded hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
- >
-
- {prd.name}
-
- ))}
-
-
- )}
-
-
-
- {/* Step 2 */}
-
-
2
-
-
{t('gettingStarted.steps.generateTasks.title')}
-
{t('gettingStarted.steps.generateTasks.description')}
-
-
-
- {/* Step 3 */}
-
-
3
-
-
{t('gettingStarted.steps.analyzeTasks.title')}
-
{t('gettingStarted.steps.analyzeTasks.description')}
-
-
-
- {/* Step 4 */}
-
-
4
-
-
{t('gettingStarted.steps.startBuilding.title')}
-
{t('gettingStarted.steps.startBuilding.description')}
-
-
-
-
-
- {
- e.preventDefault();
- e.stopPropagation();
- onShowPRDEditor?.();
- }}
- className="flex items-center gap-2 px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg font-medium transition-colors cursor-pointer"
- style={{ zIndex: 10 }}
- >
-
- {t('buttons.addPRD')}
-
-
-
-
-
-
-
- {t('gettingStarted.tip')}
-
-
-
- )}
-
- {/* TaskMaster CLI Setup Modal */}
- {showCLI && (
-
-
- {/* Modal Header */}
-
-
-
-
-
-
-
{t('setupModal.title')}
-
{t('setupModal.subtitle', { projectName: currentProject?.displayName })}
-
-
-
{
- setShowCLI(false);
- // Refresh project data after closing CLI to detect TaskMaster initialization
- setTimeout(() => {
- refreshProjects();
- // Also refresh the current project's TaskMaster status
- if (currentProject) {
- setCurrentProject(currentProject);
- }
- }, 1000);
- }}
- className="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800"
- >
-
-
-
-
- {/* Terminal Container */}
-
-
{
- // Focus the terminal when clicked
- const terminalElement = e.currentTarget.querySelector('.xterm-screen');
- if (terminalElement) {
- terminalElement.focus();
- }
- }}
- >
- {
- setIsTaskMasterComplete(true);
- if (exitCode === 0) {
- // Auto-refresh after successful completion
- setTimeout(() => {
- refreshProjects();
- if (currentProject) {
- setCurrentProject(currentProject);
- }
- }, 1000);
- }
- }}
- />
-
-
-
- {/* Modal Footer */}
-
-
-
- {isTaskMasterComplete ? (
-
-
- {t('setupModal.completed')}
-
- ) : (
- t('setupModal.willStart')
- )}
-
-
{
- setShowCLI(false);
- setIsTaskMasterComplete(false); // Reset state
- // Refresh project data after closing CLI to detect TaskMaster initialization
- setTimeout(() => {
- refreshProjects();
- // Also refresh the current project's TaskMaster status
- if (currentProject) {
- setCurrentProject(currentProject);
- }
- }, 1000);
- }}
- className={cn(
- "px-4 py-2 text-sm font-medium rounded-md transition-colors",
- isTaskMasterComplete
- ? "bg-green-600 hover:bg-green-700 text-white"
- : "text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600"
- )}
- >
- {isTaskMasterComplete ? t('setupModal.closeContinueButton') : t('setupModal.closeButton')}
-
-
-
-
-
- )}
-
- );
- }
-
- return (
-
- {/* Header Controls */}
-
- {/* Search Bar */}
-
-
- setSearchTerm(e.target.value)}
- className="pl-10 pr-4 py-2 w-full border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
- />
-
-
- {/* Controls */}
-
- {/* View Toggle */}
-
- setViewMode('kanban')}
- className={cn(
- 'p-2 rounded-md transition-colors',
- viewMode === 'kanban'
- ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
- : 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300'
- )}
- title={t('views.kanban')}
- >
-
-
- setViewMode('list')}
- className={cn(
- 'p-2 rounded-md transition-colors',
- viewMode === 'list'
- ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
- : 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300'
- )}
- title={t('views.list')}
- >
-
-
- setViewMode('grid')}
- className={cn(
- 'p-2 rounded-md transition-colors',
- viewMode === 'grid'
- ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
- : 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300'
- )}
- title={t('views.grid')}
- >
-
-
-
-
- {/* Filters Toggle */}
-
setShowFilters(!showFilters)}
- className={cn(
- 'flex items-center gap-2 px-3 py-2 rounded-lg border transition-colors',
- showFilters
- ? 'bg-blue-50 dark:bg-blue-900 border-blue-200 dark:border-blue-700 text-blue-700 dark:text-blue-300'
- : 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700'
- )}
- >
-
- {t('filters.button')}
-
-
-
- {/* Action Buttons */}
- {currentProject && (
- <>
- {/* Help Button */}
-
setShowHelpGuide(true)}
- className="p-2 text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors border border-gray-300 dark:border-gray-600"
- title={t('buttons.help')}
- >
-
-
-
- {/* PRD Management */}
-
- {existingPRDs.length > 0 ? (
- // Dropdown when PRDs exist
-
-
setShowPRDDropdown(!showPRDDropdown)}
- className="flex items-center gap-2 px-3 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition-colors font-medium"
- title={t('buttons.prdsAvailable', { count: existingPRDs.length })}
- >
-
- {t('buttons.prds')}
-
- {existingPRDs.length}
-
-
-
-
- {showPRDDropdown && (
-
-
-
{
- onShowPRDEditor?.();
- setShowPRDDropdown(false);
- }}
- className="w-full text-left px-3 py-2 text-sm font-medium text-purple-700 dark:text-purple-300 hover:bg-purple-50 dark:hover:bg-purple-900/30 rounded flex items-center gap-2"
- >
-
- {t('buttons.createNewPRD')}
-
-
-
{t('gettingStarted.steps.createPRD.existingPRDs')}
- {existingPRDs.map((prd) => (
-
{
- void loadPRDOptions(prd, true);
- }}
- className="w-full text-left px-3 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded flex items-center gap-2"
- title={t('prd.modified', { date: new Date(prd.modified).toLocaleDateString() })}
- >
-
- {prd.name}
-
- ))}
-
-
- )}
-
- ) : (
- // Simple button when no PRDs exist
-
{
- onShowPRDEditor?.();
- }}
- className="flex items-center gap-2 px-3 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition-colors font-medium"
- title={t('buttons.addPRD')}
- >
-
- {t('buttons.addPRD')}
-
- )}
-
-
- {/* Add Task Button */}
- {((currentProject?.taskMasterConfigured || currentProject?.taskmaster?.hasTaskmaster || projectTaskMaster?.hasTaskmaster) || tasks.length > 0) && (
-
setShowCreateModal(true)}
- className="flex items-center gap-2 px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors font-medium"
- title={t('buttons.addTask')}
- >
-
- {t('buttons.addTask')}
-
- )}
- >
- )}
-
-
-
- {/* Expanded Filters */}
- {showFilters && (
-
-
- {/* Status Filter */}
-
-
- {t('filters.status')}
-
- setStatusFilter(e.target.value)}
- className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
- >
- {t('filters.allStatuses')}
- {statuses.map(status => (
-
- {t(`statuses.${status}`, status.charAt(0).toUpperCase() + status.slice(1).replace('-', ' '))}
-
- ))}
-
-
-
- {/* Priority Filter */}
-
-
- {t('filters.priority')}
-
- setPriorityFilter(e.target.value)}
- className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
- >
- {t('filters.allPriorities')}
- {priorities.map(priority => (
-
- {t(`priorities.${priority}`, priority.charAt(0).toUpperCase() + priority.slice(1))}
-
- ))}
-
-
-
- {/* Sort By */}
-
-
- {t('filters.sortBy')}
-
- {
- const [field, order] = e.target.value.split('-');
- setSortBy(field);
- setSortOrder(order);
- }}
- className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
- >
- {t('sort.idAsc')}
- {t('sort.idDesc')}
- {t('sort.titleAsc')}
- {t('sort.titleDesc')}
- {t('sort.statusAsc')}
- {t('sort.statusDesc')}
- {t('sort.priorityAsc')}
- {t('sort.priorityDesc')}
-
-
-
-
- {/* Filter Actions */}
-
-
- {t('filters.showing', { filtered: filteredAndSortedTasks.length, total: tasks.length })}
-
-
- {t('filters.clearFilters')}
-
-
-
- )}
-
- {/* Quick Sort Buttons */}
-
- handleSortChange('id')}
- className={cn(
- 'flex items-center gap-1 px-3 py-1.5 rounded-md text-sm transition-colors',
- sortBy === 'id'
- ? 'bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300'
- : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
- )}
- >
- {t('sort.id')} {getSortIcon('id')}
-
- handleSortChange('status')}
- className={cn(
- 'flex items-center gap-1 px-3 py-1.5 rounded-md text-sm transition-colors',
- sortBy === 'status'
- ? 'bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300'
- : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
- )}
- >
- {t('sort.status')} {getSortIcon('status')}
-
- handleSortChange('priority')}
- className={cn(
- 'flex items-center gap-1 px-3 py-1.5 rounded-md text-sm transition-colors',
- sortBy === 'priority'
- ? 'bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300'
- : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
- )}
- >
- {t('sort.priority')} {getSortIcon('priority')}
-
-
-
- {/* Task Cards */}
- {filteredAndSortedTasks.length === 0 ? (
-
-
-
-
{t('noMatchingTasks.title')}
-
{t('noMatchingTasks.description')}
-
-
- ) : viewMode === 'kanban' ? (
- /* Kanban Board Layout - Dynamic grid based on column count */
-
= 6 && "grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6"
- )}>
- {kanbanColumns.map((column) => (
-
- {/* Column Header */}
-
-
-
- {column.title}
-
-
-
- {column.tasks.length}
-
-
-
-
-
- {/* Column Tasks */}
-
- {column.tasks.length === 0 ? (
-
-
-
- {t('kanban.noTasksYet')}
-
-
- {column.status === 'pending' ? t('kanban.tasksWillAppear') :
- column.status === 'in-progress' ? t('kanban.moveTasksHere') :
- column.status === 'done' ? t('kanban.completedTasksHere') :
- t('kanban.statusTasksHere')}
-
-
- ) : (
- column.tasks.map((task) => (
-
- onTaskClick?.(task)}
- showParent={showParentTasks}
- className="w-full shadow-sm hover:shadow-md transition-shadow cursor-pointer"
- />
-
- ))
- )}
-
-
- ))}
-
- ) : (
-
- {filteredAndSortedTasks.map((task) => (
- onTaskClick?.(task)}
- showParent={showParentTasks}
- className={viewMode === 'grid' ? 'h-full' : ''}
- />
- ))}
-
- )}
-
- {/* Create Task Modal */}
- {showCreateModal && (
-
setShowCreateModal(false)}
- onTaskCreated={() => {
- setShowCreateModal(false);
- if (onTaskCreated) onTaskCreated();
- }}
- />
- )}
-
- {/* Help Guide Modal */}
- {showHelpGuide && (
-
-
- {/* Modal Header */}
-
-
-
-
-
-
-
{t('helpGuide.title')}
-
{t('helpGuide.subtitle')}
-
-
-
setShowHelpGuide(false)}
- className="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
- >
-
-
-
-
- {/* Modal Content */}
-
-
- {/* Step 1 */}
-
-
1
-
-
{t('gettingStarted.steps.createPRD.title')}
-
{t('gettingStarted.steps.createPRD.description')}
-
{
- onShowPRDEditor?.();
- setShowHelpGuide(false);
- }}
- className="inline-flex items-center gap-2 text-sm bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 px-3 py-1.5 rounded-lg hover:bg-purple-200 dark:hover:bg-purple-900/50 transition-colors"
- >
-
- {t('buttons.addPRD')}
-
-
-
-
- {/* Step 2 */}
-
-
2
-
-
{t('gettingStarted.steps.generateTasks.title')}
-
{t('gettingStarted.steps.generateTasks.description')}
-
-
- {t('helpGuide.examples.parsePRD')}
-
-
-
-
-
- {/* Step 3 */}
-
-
3
-
-
{t('gettingStarted.steps.analyzeTasks.title')}
-
{t('gettingStarted.steps.analyzeTasks.description')}
-
-
- {t('helpGuide.examples.expandTask')}
-
-
-
-
-
- {/* Step 4 */}
-
-
4
-
-
{t('gettingStarted.steps.startBuilding.title')}
-
{t('gettingStarted.steps.startBuilding.description')}
-
-
- {t('helpGuide.examples.addTask')}
-
-
-
- {t('helpGuide.moreExamples')}
-
-
-
-
- {/* Pro Tips */}
-
-
{t('helpGuide.proTips.title')}
-
-
-
- {t('helpGuide.proTips.search')}
-
-
-
- {t('helpGuide.proTips.views')}
-
-
-
- {t('helpGuide.proTips.filters')}
-
-
-
- {t('helpGuide.proTips.details')}
-
-
-
-
- {/* Learn More Section */}
-
-
-
-
-
- )}
-
- );
-};
-
-export default TaskList;
diff --git a/src/components/TaskMasterSetupWizard.jsx b/src/components/TaskMasterSetupWizard.jsx
deleted file mode 100644
index 6bcb1738..00000000
--- a/src/components/TaskMasterSetupWizard.jsx
+++ /dev/null
@@ -1,604 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { X, ChevronRight, ChevronLeft, CheckCircle, AlertCircle, Settings, Server, FileText, Sparkles, ExternalLink, Copy } from 'lucide-react';
-import { cn } from '../lib/utils';
-import { api } from '../utils/api';
-import { copyTextToClipboard } from '../utils/clipboard';
-
-const TaskMasterSetupWizard = ({
- isOpen = true,
- onClose,
- onComplete,
- currentProject,
- className = ''
-}) => {
- const [currentStep, setCurrentStep] = useState(1);
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState(null);
- const [setupData, setSetupData] = useState({
- projectRoot: '',
- initGit: true,
- storeTasksInGit: true,
- addAliases: true,
- skipInstall: false,
- rules: ['claude'],
- mcpConfigured: false,
- prdContent: ''
- });
-
- const totalSteps = 4;
-
- useEffect(() => {
- if (currentProject) {
- setSetupData(prev => ({
- ...prev,
- projectRoot: currentProject.path || ''
- }));
- }
- }, [currentProject]);
-
- const steps = [
- {
- id: 1,
- title: 'Project Configuration',
- description: 'Configure basic TaskMaster settings for your project'
- },
- {
- id: 2,
- title: 'MCP Server Setup',
- description: 'Ensure TaskMaster MCP server is properly configured'
- },
- {
- id: 3,
- title: 'PRD Creation',
- description: 'Create or import a Product Requirements Document'
- },
- {
- id: 4,
- title: 'Complete Setup',
- description: 'Initialize TaskMaster and generate initial tasks'
- }
- ];
-
- const handleNext = async () => {
- setError(null);
-
- try {
- if (currentStep === 1) {
- // Validate project configuration
- if (!setupData.projectRoot) {
- setError('Project root path is required');
- return;
- }
- setCurrentStep(2);
- } else if (currentStep === 2) {
- // Check MCP server status
- setLoading(true);
- try {
- const mcpStatus = await api.get('/mcp-utils/taskmaster-server');
- setSetupData(prev => ({
- ...prev,
- mcpConfigured: mcpStatus.hasMCPServer && mcpStatus.isConfigured
- }));
- setCurrentStep(3);
- } catch (err) {
- setError('Failed to check MCP server status. You can continue but some features may not work.');
- setCurrentStep(3);
- }
- } else if (currentStep === 3) {
- // Validate PRD step
- if (!setupData.prdContent.trim()) {
- setError('Please create or import a PRD to continue');
- return;
- }
- setCurrentStep(4);
- } else if (currentStep === 4) {
- // Complete setup
- await completeSetup();
- }
- } catch (err) {
- setError(err.message || 'An error occurred');
- } finally {
- setLoading(false);
- }
- };
-
- const handlePrevious = () => {
- if (currentStep > 1) {
- setCurrentStep(currentStep - 1);
- setError(null);
- }
- };
-
- const completeSetup = async () => {
- setLoading(true);
- try {
- // Initialize TaskMaster project
- const initResponse = await api.post('/taskmaster/initialize', {
- projectRoot: setupData.projectRoot,
- initGit: setupData.initGit,
- storeTasksInGit: setupData.storeTasksInGit,
- addAliases: setupData.addAliases,
- skipInstall: setupData.skipInstall,
- rules: setupData.rules,
- yes: true
- });
-
- if (!initResponse.ok) {
- throw new Error('Failed to initialize TaskMaster project');
- }
-
- // Save PRD content if provided
- if (setupData.prdContent.trim()) {
- const prdResponse = await api.post('/taskmaster/save-prd', {
- projectRoot: setupData.projectRoot,
- content: setupData.prdContent
- });
-
- if (!prdResponse.ok) {
- console.warn('Failed to save PRD content');
- }
- }
-
- // Parse PRD to generate initial tasks
- if (setupData.prdContent.trim()) {
- const parseResponse = await api.post('/taskmaster/parse-prd', {
- projectRoot: setupData.projectRoot,
- input: '.taskmaster/docs/prd.txt',
- numTasks: '10',
- research: false,
- force: false
- });
-
- if (!parseResponse.ok) {
- console.warn('Failed to parse PRD and generate tasks');
- }
- }
-
- onComplete?.();
- onClose?.();
- } catch (err) {
- setError(err.message || 'Failed to complete TaskMaster setup');
- } finally {
- setLoading(false);
- }
- };
-
- const copyMCPConfig = () => {
- const mcpConfig = `{
- "mcpServers": {
- "": {
- "command": "npx",
- "args": ["-y", "--package=task-master-ai", "task-master-ai"],
- "env": {
- "ANTHROPIC_API_KEY": "your_anthropic_key_here",
- "PERPLEXITY_API_KEY": "your_perplexity_key_here"
- }
- }
- }
-}`;
- copyTextToClipboard(mcpConfig);
- };
-
- const renderStepContent = () => {
- switch (currentStep) {
- case 1:
- return (
-
-
-
-
- Project Configuration
-
-
- Configure TaskMaster settings for your project
-
-
-
-
-
-
- Project Root Path
-
- setSetupData(prev => ({ ...prev, projectRoot: e.target.value }))}
- className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
- placeholder="/path/to/your/project"
- />
-
-
-
-
Options
-
-
- setSetupData(prev => ({ ...prev, initGit: e.target.checked }))}
- className="w-4 h-4 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
- />
- Initialize Git repository
-
-
-
- setSetupData(prev => ({ ...prev, storeTasksInGit: e.target.checked }))}
- className="w-4 h-4 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
- />
- Store tasks in Git
-
-
-
- setSetupData(prev => ({ ...prev, addAliases: e.target.checked }))}
- className="w-4 h-4 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
- />
- Add shell aliases (tm, taskmaster)
-
-
-
-
-
- Rule Profiles
-
-
- {['claude', 'cursor', 'vscode', 'roo', 'cline', 'windsurf'].map(rule => (
-
- {
- if (e.target.checked) {
- setSetupData(prev => ({ ...prev, rules: [...prev.rules, rule] }));
- } else {
- setSetupData(prev => ({ ...prev, rules: prev.rules.filter(r => r !== rule) }));
- }
- }}
- className="w-4 h-4 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
- />
- {rule}
-
- ))}
-
-
-
-
- );
-
- case 2:
- return (
-
-
-
-
- MCP Server Setup
-
-
- TaskMaster works best with the MCP server configured
-
-
-
-
-
-
-
-
- MCP Server Configuration
-
-
- To enable full TaskMaster integration, add the MCP server configuration to your Claude settings.
-
-
-
-
- .mcp.json
-
-
- Copy
-
-
-
-{`{
- "mcpServers": {
- "task-master-ai": {
- "command": "npx",
- "args": ["-y", "--package=task-master-ai", "task-master-ai"],
- "env": {
- "ANTHROPIC_API_KEY": "your_anthropic_key_here",
- "PERPLEXITY_API_KEY": "your_perplexity_key_here"
- }
- }
- }
-}`}
-
-
-
-
-
-
-
-
-
-
Current Status
-
- {setupData.mcpConfigured ? (
- <>
-
-
MCP server is configured
- >
- ) : (
- <>
-
-
MCP server not detected (optional)
- >
- )}
-
-
-
- );
-
- case 3:
- return (
-
-
-
-
- Product Requirements Document
-
-
- Create or import a PRD to generate initial tasks
-
-
-
-
-
-
- PRD Content
-
- setSetupData(prev => ({ ...prev, prdContent: e.target.value }))}
- rows={12}
- className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-white font-mono text-sm"
- placeholder="# Product Requirements Document
-
-## 1. Overview
-Describe your project or feature...
-
-## 2. Objectives
-- Primary goal
-- Success metrics
-
-## 3. User Stories
-- As a user, I want...
-
-## 4. Requirements
-- Feature requirements
-- Technical requirements
-
-## 5. Implementation Plan
-- Phase 1: Core features
-- Phase 2: Enhancements"
- />
-
-
-
-
-
-
-
- AI Task Generation
-
-
- TaskMaster will analyze your PRD and automatically generate a structured task list with dependencies, priorities, and implementation details.
-
-
-
-
-
-
- );
-
- case 4:
- return (
-
-
-
-
- Complete Setup
-
-
- Ready to initialize TaskMaster for your project
-
-
-
-
-
- Setup Summary
-
-
-
-
- Project: {setupData.projectRoot}
-
-
-
- Rules: {setupData.rules.join(', ')}
-
- {setupData.mcpConfigured && (
-
-
- MCP server configured
-
- )}
-
-
- PRD content ready ({setupData.prdContent.length} characters)
-
-
-
-
-
-
- What happens next?
-
-
- Initialize TaskMaster project structure
- Save your PRD to .taskmaster/docs/prd.txt
- Generate initial tasks from your PRD
- Set up project configuration and rules
-
-
-
- );
-
- default:
- return null;
- }
- };
-
- if (!isOpen) return null;
-
- return (
-
-
- {/* Header */}
-
-
-
-
-
- TaskMaster Setup Wizard
-
-
- Step {currentStep} of {totalSteps}: {steps[currentStep - 1]?.description}
-
-
-
-
-
-
-
-
-
- {/* Progress Bar */}
-
-
- {steps.map((step, index) => (
-
-
step.id
- ? 'bg-green-500 text-white'
- : currentStep === step.id
- ? 'bg-blue-500 text-white'
- : 'bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400'
- )}>
- {currentStep > step.id ? (
-
- ) : (
- step.id
- )}
-
- {index < steps.length - 1 && (
-
step.id
- ? 'bg-green-500'
- : 'bg-gray-200 dark:bg-gray-700'
- )} />
- )}
-
- ))}
-
-
- {steps.map(step => (
-
- {step.title}
-
- ))}
-
-
-
- {/* Content */}
-
- {renderStepContent()}
-
- {error && (
-
- )}
-
-
- {/* Footer */}
-
-
-
- Previous
-
-
-
- {currentStep} of {totalSteps}
-
-
-
- {loading ? (
- <>
-
- {currentStep === totalSteps ? 'Setting up...' : 'Processing...'}
- >
- ) : (
- <>
- {currentStep === totalSteps ? 'Complete Setup' : 'Next'}
-
- >
- )}
-
-
-
-
- );
-};
-
-export default TaskMasterSetupWizard;
\ No newline at end of file
diff --git a/src/components/TaskMasterStatus.jsx b/src/components/TaskMasterStatus.jsx
deleted file mode 100644
index 27c953d5..00000000
--- a/src/components/TaskMasterStatus.jsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import React from 'react';
-import { useTaskMaster } from '../contexts/TaskMasterContext';
-import TaskIndicator from './TaskIndicator';
-
-const TaskMasterStatus = () => {
- const {
- currentProject,
- projectTaskMaster,
- mcpServerStatus,
- isLoading,
- isLoadingMCP,
- error
- } = useTaskMaster();
-
- if (isLoading || isLoadingMCP) {
- return (
-
-
- Loading TaskMaster status...
-
- );
- }
-
- if (error) {
- return (
-
-
- TaskMaster Error
-
- );
- }
-
- // Show MCP server status
- const mcpConfigured = mcpServerStatus?.hasMCPServer && mcpServerStatus?.isConfigured;
-
- // Show project TaskMaster status
- const projectConfigured = currentProject?.taskmaster?.hasTaskmaster;
- const taskCount = currentProject?.taskmaster?.metadata?.taskCount || 0;
- const completedCount = currentProject?.taskmaster?.metadata?.completed || 0;
-
- if (!currentProject) {
- return (
-
-
- No project selected
-
- );
- }
-
- // Determine overall status for TaskIndicator
- let overallStatus = 'not-configured';
- if (projectConfigured && mcpConfigured) {
- overallStatus = 'fully-configured';
- } else if (projectConfigured) {
- overallStatus = 'taskmaster-only';
- } else if (mcpConfigured) {
- overallStatus = 'mcp-only';
- }
-
- return (
-
- {/* TaskMaster Status Indicator */}
-
-
- {/* Task Progress Info */}
- {projectConfigured && (
-
-
- {completedCount}/{taskCount} tasks
-
- {taskCount > 0 && (
-
- ({Math.round((completedCount / taskCount) * 100)}%)
-
- )}
-
- )}
-
- );
-};
-
-export default TaskMasterStatus;
\ No newline at end of file
diff --git a/src/components/TasksSettings.jsx b/src/components/TasksSettings.jsx
deleted file mode 100644
index e341115e..00000000
--- a/src/components/TasksSettings.jsx
+++ /dev/null
@@ -1,3 +0,0 @@
-import TasksSettingsTab from './settings/view/tabs/tasks-settings/TasksSettingsTab';
-
-export default TasksSettingsTab;
diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx
deleted file mode 100644
index 8876c15a..00000000
--- a/src/components/TodoList.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import React from 'react';
-import { Badge } from './ui/badge';
-import { CheckCircle2, Clock, Circle } from 'lucide-react';
-
-const TodoList = ({ todos, isResult = false }) => {
- if (!todos || !Array.isArray(todos)) {
- return null;
- }
-
- const getStatusIcon = (status) => {
- switch (status) {
- case 'completed':
- return
;
- case 'in_progress':
- return
;
- case 'pending':
- default:
- return
;
- }
- };
-
- const getStatusColor = (status) => {
- switch (status) {
- case 'completed':
- return 'bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-200 border-green-200 dark:border-green-800';
- case 'in_progress':
- return 'bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 border-blue-200 dark:border-blue-800';
- case 'pending':
- default:
- return 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-700';
- }
- };
-
- const getPriorityColor = (priority) => {
- switch (priority) {
- case 'high':
- return 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 border-red-200 dark:border-red-800';
- case 'medium':
- return 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-300 border-yellow-200 dark:border-yellow-800';
- case 'low':
- default:
- return 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-700';
- }
- };
-
- return (
-
- {isResult && (
-
- Todo List ({todos.length} {todos.length === 1 ? 'item' : 'items'})
-
- )}
-
- {todos.map((todo, index) => (
-
-
- {getStatusIcon(todo.status)}
-
-
-
-
-
- {todo.content}
-
-
-
-
- {todo.priority}
-
-
- {todo.status.replace('_', ' ')}
-
-
-
-
-
- ))}
-
- );
-};
-
-export default TodoList;
diff --git a/src/components/Tooltip.jsx b/src/components/Tooltip.jsx
deleted file mode 100644
index 10421a84..00000000
--- a/src/components/Tooltip.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import React, { useState } from 'react';
-import { cn } from '../lib/utils';
-
-const Tooltip = ({
- children,
- content,
- position = 'top',
- className = '',
- delay = 500
-}) => {
- const [isVisible, setIsVisible] = useState(false);
- const [timeoutId, setTimeoutId] = useState(null);
-
- const handleMouseEnter = () => {
- const id = setTimeout(() => {
- setIsVisible(true);
- }, delay);
- setTimeoutId(id);
- };
-
- const handleMouseLeave = () => {
- if (timeoutId) {
- clearTimeout(timeoutId);
- setTimeoutId(null);
- }
- setIsVisible(false);
- };
-
- const getPositionClasses = () => {
- switch (position) {
- case 'top':
- return 'bottom-full left-1/2 transform -translate-x-1/2 mb-2';
- case 'bottom':
- return 'top-full left-1/2 transform -translate-x-1/2 mt-2';
- case 'left':
- return 'right-full top-1/2 transform -translate-y-1/2 mr-2';
- case 'right':
- return 'left-full top-1/2 transform -translate-y-1/2 ml-2';
- default:
- return 'bottom-full left-1/2 transform -translate-x-1/2 mb-2';
- }
- };
-
- const getArrowClasses = () => {
- switch (position) {
- case 'top':
- return 'top-full left-1/2 transform -translate-x-1/2 border-t-gray-900 dark:border-t-gray-100';
- case 'bottom':
- return 'bottom-full left-1/2 transform -translate-x-1/2 border-b-gray-900 dark:border-b-gray-100';
- case 'left':
- return 'left-full top-1/2 transform -translate-y-1/2 border-l-gray-900 dark:border-l-gray-100';
- case 'right':
- return 'right-full top-1/2 transform -translate-y-1/2 border-r-gray-900 dark:border-r-gray-100';
- default:
- return 'top-full left-1/2 transform -translate-x-1/2 border-t-gray-900 dark:border-t-gray-100';
- }
- };
-
- if (!content) {
- return children;
- }
-
- return (
-
- {children}
-
- {isVisible && (
-
- {content}
-
- {/* Arrow */}
-
-
- )}
-
- );
-};
-
-export default Tooltip;
\ No newline at end of file
diff --git a/src/components/app/AppContent.tsx b/src/components/app/AppContent.tsx
index 1168919a..f2806c6a 100644
--- a/src/components/app/AppContent.tsx
+++ b/src/components/app/AppContent.tsx
@@ -1,15 +1,13 @@
import { useEffect, useRef } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
-
import Sidebar from '../sidebar/view/Sidebar';
import MainContent from '../main-content/view/MainContent';
-import MobileNav from '../MobileNav';
-
import { useWebSocket } from '../../contexts/WebSocketContext';
import { useDeviceSettings } from '../../hooks/useDeviceSettings';
import { useSessionProtection } from '../../hooks/useSessionProtection';
import { useProjectsState } from '../../hooks/useProjectsState';
+import MobileNav from './MobileNav';
export default function AppContent() {
const navigate = useNavigate();
@@ -98,7 +96,7 @@ export default function AppContent() {
) : (
event.stopPropagation()}
onTouchStart={(event) => event.stopPropagation()}
@@ -125,7 +123,7 @@ export default function AppContent() {
)}
-
+
>;
+ isInputFocused: boolean;
+};
+
+export default function MobileNav({ activeTab, setActiveTab, isInputFocused }: MobileNavProps) {
const { tasksEnabled, isTaskMasterInstalled } = useTasksSettings();
const shouldShowTasksTab = Boolean(tasksEnabled && isTaskMasterInstalled);
@@ -42,12 +48,11 @@ function MobileNav({ activeTab, setActiveTab, isInputFocused }) {
return (
-
+
{navItems.map((item) => {
const Icon = item.icon;
const isActive = activeTab === item.id;
@@ -60,19 +65,18 @@ function MobileNav({ activeTab, setActiveTab, isInputFocused }) {
e.preventDefault();
item.onClick();
}}
- className={`flex flex-col items-center justify-center gap-0.5 px-3 py-2 rounded-xl flex-1 relative touch-manipulation transition-all duration-200 active:scale-95 ${
- isActive
- ? 'text-primary'
- : 'text-muted-foreground hover:text-foreground'
- }`}
+ className={`relative flex flex-1 touch-manipulation flex-col items-center justify-center gap-0.5 rounded-xl px-3 py-2 transition-all duration-200 active:scale-95 ${isActive
+ ? 'text-primary'
+ : 'text-muted-foreground hover:text-foreground'
+ }`}
aria-label={item.label}
aria-current={isActive ? 'page' : undefined}
>
{isActive && (
-
+
)}
@@ -86,5 +90,3 @@ function MobileNav({ activeTab, setActiveTab, isInputFocused }) {
);
}
-
-export default MobileNav;
diff --git a/src/components/auth/constants.ts b/src/components/auth/constants.ts
new file mode 100644
index 00000000..37226b02
--- /dev/null
+++ b/src/components/auth/constants.ts
@@ -0,0 +1,8 @@
+export const AUTH_TOKEN_STORAGE_KEY = 'auth-token';
+
+export const AUTH_ERROR_MESSAGES = {
+ authStatusCheckFailed: 'Failed to check authentication status',
+ loginFailed: 'Login failed',
+ registrationFailed: 'Registration failed',
+ networkError: 'Network error. Please try again.',
+} as const;
diff --git a/src/components/auth/context/AuthContext.tsx b/src/components/auth/context/AuthContext.tsx
new file mode 100644
index 00000000..69bdd8a5
--- /dev/null
+++ b/src/components/auth/context/AuthContext.tsx
@@ -0,0 +1,222 @@
+import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
+import { IS_PLATFORM } from '../../../constants/config';
+import { api } from '../../../utils/api';
+import { AUTH_ERROR_MESSAGES, AUTH_TOKEN_STORAGE_KEY } from '../constants';
+import type {
+ AuthContextValue,
+ AuthProviderProps,
+ AuthSessionPayload,
+ AuthStatusPayload,
+ AuthUser,
+ AuthUserPayload,
+ OnboardingStatusPayload,
+} from '../types';
+import { parseJsonSafely, resolveApiErrorMessage } from '../utils';
+
+const AuthContext = createContext
(null);
+
+const readStoredToken = (): string | null => localStorage.getItem(AUTH_TOKEN_STORAGE_KEY);
+
+const persistToken = (token: string) => {
+ localStorage.setItem(AUTH_TOKEN_STORAGE_KEY, token);
+};
+
+const clearStoredToken = () => {
+ localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
+};
+
+export function useAuth(): AuthContextValue {
+ const context = useContext(AuthContext);
+ if (!context) {
+ throw new Error('useAuth must be used within an AuthProvider');
+ }
+
+ return context;
+}
+
+export function AuthProvider({ children }: AuthProviderProps) {
+ const [user, setUser] = useState(null);
+ const [token, setToken] = useState(() => readStoredToken());
+ const [isLoading, setIsLoading] = useState(true);
+ const [needsSetup, setNeedsSetup] = useState(false);
+ const [hasCompletedOnboarding, setHasCompletedOnboarding] = useState(true);
+ const [error, setError] = useState(null);
+
+ const setSession = useCallback((nextUser: AuthUser, nextToken: string) => {
+ setUser(nextUser);
+ setToken(nextToken);
+ persistToken(nextToken);
+ }, []);
+
+ const clearSession = useCallback(() => {
+ setUser(null);
+ setToken(null);
+ clearStoredToken();
+ }, []);
+
+ const checkOnboardingStatus = useCallback(async () => {
+ try {
+ const response = await api.user.onboardingStatus();
+ if (!response.ok) {
+ return;
+ }
+
+ const payload = await parseJsonSafely(response);
+ setHasCompletedOnboarding(Boolean(payload?.hasCompletedOnboarding));
+ } catch (caughtError) {
+ console.error('Error checking onboarding status:', caughtError);
+ // Fail open to avoid blocking access on transient onboarding status errors.
+ setHasCompletedOnboarding(true);
+ }
+ }, []);
+
+ const refreshOnboardingStatus = useCallback(async () => {
+ await checkOnboardingStatus();
+ }, [checkOnboardingStatus]);
+
+ const checkAuthStatus = useCallback(async () => {
+ try {
+ setIsLoading(true);
+ setError(null);
+
+ const statusResponse = await api.auth.status();
+ const statusPayload = await parseJsonSafely(statusResponse);
+
+ if (statusPayload?.needsSetup) {
+ setNeedsSetup(true);
+ return;
+ }
+
+ setNeedsSetup(false);
+
+ if (!token) {
+ return;
+ }
+
+ const userResponse = await api.auth.user();
+ if (!userResponse.ok) {
+ clearSession();
+ return;
+ }
+
+ const userPayload = await parseJsonSafely(userResponse);
+ if (!userPayload?.user) {
+ clearSession();
+ return;
+ }
+
+ setUser(userPayload.user);
+ await checkOnboardingStatus();
+ } catch (caughtError) {
+ console.error('[Auth] Auth status check failed:', caughtError);
+ setError(AUTH_ERROR_MESSAGES.authStatusCheckFailed);
+ } finally {
+ setIsLoading(false);
+ }
+ }, [checkOnboardingStatus, clearSession, token]);
+
+ useEffect(() => {
+ if (IS_PLATFORM) {
+ setUser({ username: 'platform-user' });
+ setNeedsSetup(false);
+ void checkOnboardingStatus().finally(() => {
+ setIsLoading(false);
+ });
+ return;
+ }
+
+ void checkAuthStatus();
+ }, [checkAuthStatus, checkOnboardingStatus]);
+
+ const login = useCallback(
+ async (username, password) => {
+ try {
+ setError(null);
+ const response = await api.auth.login(username, password);
+ const payload = await parseJsonSafely(response);
+
+ if (!response.ok || !payload?.token || !payload.user) {
+ const message = resolveApiErrorMessage(payload, AUTH_ERROR_MESSAGES.loginFailed);
+ setError(message);
+ return { success: false, error: message };
+ }
+
+ setSession(payload.user, payload.token);
+ setNeedsSetup(false);
+ await checkOnboardingStatus();
+ return { success: true };
+ } catch (caughtError) {
+ console.error('Login error:', caughtError);
+ setError(AUTH_ERROR_MESSAGES.networkError);
+ return { success: false, error: AUTH_ERROR_MESSAGES.networkError };
+ }
+ },
+ [checkOnboardingStatus, setSession],
+ );
+
+ const register = useCallback(
+ async (username, password) => {
+ try {
+ setError(null);
+ const response = await api.auth.register(username, password);
+ const payload = await parseJsonSafely(response);
+
+ if (!response.ok || !payload?.token || !payload.user) {
+ const message = resolveApiErrorMessage(payload, AUTH_ERROR_MESSAGES.registrationFailed);
+ setError(message);
+ return { success: false, error: message };
+ }
+
+ setSession(payload.user, payload.token);
+ setNeedsSetup(false);
+ await checkOnboardingStatus();
+ return { success: true };
+ } catch (caughtError) {
+ console.error('Registration error:', caughtError);
+ setError(AUTH_ERROR_MESSAGES.networkError);
+ return { success: false, error: AUTH_ERROR_MESSAGES.networkError };
+ }
+ },
+ [checkOnboardingStatus, setSession],
+ );
+
+ const logout = useCallback(() => {
+ const tokenToInvalidate = token;
+ clearSession();
+
+ if (tokenToInvalidate) {
+ void api.auth.logout().catch((caughtError: unknown) => {
+ console.error('Logout endpoint error:', caughtError);
+ });
+ }
+ }, [clearSession, token]);
+
+ const contextValue = useMemo(
+ () => ({
+ user,
+ token,
+ isLoading,
+ needsSetup,
+ hasCompletedOnboarding,
+ error,
+ login,
+ register,
+ logout,
+ refreshOnboardingStatus,
+ }),
+ [
+ error,
+ hasCompletedOnboarding,
+ isLoading,
+ login,
+ logout,
+ needsSetup,
+ refreshOnboardingStatus,
+ register,
+ token,
+ user,
+ ],
+ );
+
+ return {children} ;
+}
diff --git a/src/components/auth/index.ts b/src/components/auth/index.ts
new file mode 100644
index 00000000..62659ecf
--- /dev/null
+++ b/src/components/auth/index.ts
@@ -0,0 +1,2 @@
+export { AuthProvider, useAuth } from './context/AuthContext';
+export { default as ProtectedRoute } from './view/ProtectedRoute';
diff --git a/src/components/auth/types.ts b/src/components/auth/types.ts
new file mode 100644
index 00000000..e745a372
--- /dev/null
+++ b/src/components/auth/types.ts
@@ -0,0 +1,50 @@
+import type { ReactNode } from 'react';
+
+export type AuthUser = {
+ id?: number | string;
+ username: string;
+ [key: string]: unknown;
+};
+
+export type AuthActionResult = { success: true } | { success: false; error: string };
+
+export type AuthSessionPayload = {
+ token?: string;
+ user?: AuthUser;
+ error?: string;
+ message?: string;
+};
+
+export type AuthStatusPayload = {
+ needsSetup?: boolean;
+};
+
+export type AuthUserPayload = {
+ user?: AuthUser;
+};
+
+export type OnboardingStatusPayload = {
+ hasCompletedOnboarding?: boolean;
+};
+
+export type ApiErrorPayload = {
+ error?: string;
+ message?: string;
+};
+
+export type AuthContextValue = {
+ user: AuthUser | null;
+ token: string | null;
+ isLoading: boolean;
+ needsSetup: boolean;
+ hasCompletedOnboarding: boolean;
+ error: string | null;
+ login: (username: string, password: string) => Promise;
+ register: (username: string, password: string) => Promise;
+ logout: () => void;
+ refreshOnboardingStatus: () => Promise;
+};
+
+export type AuthProviderProps = {
+ children: ReactNode;
+};
diff --git a/src/components/auth/utils.ts b/src/components/auth/utils.ts
new file mode 100644
index 00000000..152a986c
--- /dev/null
+++ b/src/components/auth/utils.ts
@@ -0,0 +1,17 @@
+import type { ApiErrorPayload } from './types';
+
+export async function parseJsonSafely(response: Response): Promise {
+ try {
+ return (await response.json()) as T;
+ } catch {
+ return null;
+ }
+}
+
+export function resolveApiErrorMessage(payload: ApiErrorPayload | null, fallback: string): string {
+ if (!payload) {
+ return fallback;
+ }
+
+ return payload.error ?? payload.message ?? fallback;
+}
diff --git a/src/components/auth/view/AuthErrorAlert.tsx b/src/components/auth/view/AuthErrorAlert.tsx
new file mode 100644
index 00000000..96a6be41
--- /dev/null
+++ b/src/components/auth/view/AuthErrorAlert.tsx
@@ -0,0 +1,15 @@
+type AuthErrorAlertProps = {
+ errorMessage: string;
+};
+
+export default function AuthErrorAlert({ errorMessage }: AuthErrorAlertProps) {
+ if (!errorMessage) {
+ return null;
+ }
+
+ return (
+
+ );
+}
diff --git a/src/components/auth/view/AuthInputField.tsx b/src/components/auth/view/AuthInputField.tsx
new file mode 100644
index 00000000..9ffcfc83
--- /dev/null
+++ b/src/components/auth/view/AuthInputField.tsx
@@ -0,0 +1,37 @@
+type AuthInputFieldProps = {
+ id: string;
+ label: string;
+ value: string;
+ onChange: (nextValue: string) => void;
+ placeholder: string;
+ isDisabled: boolean;
+ type?: 'text' | 'password' | 'email';
+};
+
+export default function AuthInputField({
+ id,
+ label,
+ value,
+ onChange,
+ placeholder,
+ isDisabled,
+ type = 'text',
+}: AuthInputFieldProps) {
+ return (
+
+
+ {label}
+
+ onChange(event.target.value)}
+ className="w-full rounded-md border border-border bg-background px-3 py-2 text-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500"
+ placeholder={placeholder}
+ required
+ disabled={isDisabled}
+ />
+
+ );
+}
diff --git a/src/components/auth/view/AuthLoadingScreen.tsx b/src/components/auth/view/AuthLoadingScreen.tsx
new file mode 100644
index 00000000..36ecbc2f
--- /dev/null
+++ b/src/components/auth/view/AuthLoadingScreen.tsx
@@ -0,0 +1,31 @@
+import { MessageSquare } from 'lucide-react';
+
+const loadingDotAnimationDelays = ['0s', '0.1s', '0.2s'];
+
+export default function AuthLoadingScreen() {
+ return (
+
+
+
+
+
Claude Code UI
+
+
+ {loadingDotAnimationDelays.map((delay) => (
+
+ ))}
+
+
+
Loading...
+
+
+ );
+}
diff --git a/src/components/auth/view/AuthScreenLayout.tsx b/src/components/auth/view/AuthScreenLayout.tsx
new file mode 100644
index 00000000..6651dbe0
--- /dev/null
+++ b/src/components/auth/view/AuthScreenLayout.tsx
@@ -0,0 +1,44 @@
+import type { ReactNode } from 'react';
+import { MessageSquare } from 'lucide-react';
+
+type AuthScreenLayoutProps = {
+ title: string;
+ description: string;
+ children: ReactNode;
+ footerText: string;
+ logo?: ReactNode;
+};
+
+export default function AuthScreenLayout({
+ title,
+ description,
+ children,
+ footerText,
+ logo,
+}: AuthScreenLayoutProps) {
+ return (
+
+
+
+
+
+ {logo ?? (
+
+
+
+ )}
+
+
{title}
+
{description}
+
+
+ {children}
+
+
+
+
+
+ );
+}
diff --git a/src/components/auth/view/LoginForm.tsx b/src/components/auth/view/LoginForm.tsx
new file mode 100644
index 00000000..633c9728
--- /dev/null
+++ b/src/components/auth/view/LoginForm.tsx
@@ -0,0 +1,90 @@
+import { useCallback, useState } from 'react';
+import type { FormEvent } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useAuth } from '../context/AuthContext';
+import AuthErrorAlert from './AuthErrorAlert';
+import AuthInputField from './AuthInputField';
+import AuthScreenLayout from './AuthScreenLayout';
+
+type LoginFormState = {
+ username: string;
+ password: string;
+};
+
+const initialState: LoginFormState = {
+ username: '',
+ password: '',
+};
+
+export default function LoginForm() {
+ const { t } = useTranslation('auth');
+ const { login } = useAuth();
+
+ const [formState, setFormState] = useState(initialState);
+ const [errorMessage, setErrorMessage] = useState('');
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const updateField = useCallback((field: keyof LoginFormState, value: string) => {
+ setFormState((previous) => ({ ...previous, [field]: value }));
+ }, []);
+
+ const handleSubmit = useCallback(
+ async (event: FormEvent) => {
+ event.preventDefault();
+ setErrorMessage('');
+
+ // Keep form validation local so each auth screen owns its own UI feedback.
+ if (!formState.username.trim() || !formState.password) {
+ setErrorMessage(t('login.errors.requiredFields'));
+ return;
+ }
+
+ setIsSubmitting(true);
+ const result = await login(formState.username.trim(), formState.password);
+ if (!result.success) {
+ setErrorMessage(result.error);
+ }
+ setIsSubmitting(false);
+ },
+ [formState.password, formState.username, login, t],
+ );
+
+ return (
+
+
+ updateField('username', value)}
+ placeholder={t('login.placeholders.username')}
+ isDisabled={isSubmitting}
+ />
+
+ updateField('password', value)}
+ placeholder={t('login.placeholders.password')}
+ isDisabled={isSubmitting}
+ type="password"
+ />
+
+
+
+
+ {isSubmitting ? t('login.loading') : t('login.submit')}
+
+
+
+ );
+}
diff --git a/src/components/auth/view/ProtectedRoute.tsx b/src/components/auth/view/ProtectedRoute.tsx
new file mode 100644
index 00000000..d94dcaf1
--- /dev/null
+++ b/src/components/auth/view/ProtectedRoute.tsx
@@ -0,0 +1,41 @@
+import type { ReactNode } from 'react';
+import { IS_PLATFORM } from '../../../constants/config';
+import { useAuth } from '../context/AuthContext';
+import Onboarding from '../../onboarding/view/Onboarding';
+import AuthLoadingScreen from './AuthLoadingScreen';
+import LoginForm from './LoginForm';
+import SetupForm from './SetupForm';
+
+type ProtectedRouteProps = {
+ children: ReactNode;
+};
+
+export default function ProtectedRoute({ children }: ProtectedRouteProps) {
+ const { user, isLoading, needsSetup, hasCompletedOnboarding, refreshOnboardingStatus } = useAuth();
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (IS_PLATFORM) {
+ if (!hasCompletedOnboarding) {
+ return ;
+ }
+
+ return <>{children}>;
+ }
+
+ if (needsSetup) {
+ return ;
+ }
+
+ if (!user) {
+ return ;
+ }
+
+ if (!hasCompletedOnboarding) {
+ return ;
+ }
+
+ return <>{children}>;
+}
diff --git a/src/components/auth/view/SetupForm.tsx b/src/components/auth/view/SetupForm.tsx
new file mode 100644
index 00000000..a67a8be6
--- /dev/null
+++ b/src/components/auth/view/SetupForm.tsx
@@ -0,0 +1,121 @@
+import { useCallback, useState } from 'react';
+import type { FormEvent } from 'react';
+import { useAuth } from '../context/AuthContext';
+import AuthErrorAlert from './AuthErrorAlert';
+import AuthInputField from './AuthInputField';
+import AuthScreenLayout from './AuthScreenLayout';
+
+type SetupFormState = {
+ username: string;
+ password: string;
+ confirmPassword: string;
+};
+
+const initialState: SetupFormState = {
+ username: '',
+ password: '',
+ confirmPassword: '',
+};
+
+function validateSetupForm(formState: SetupFormState): string | null {
+ if (!formState.username.trim() || !formState.password || !formState.confirmPassword) {
+ return 'Please fill in all fields.';
+ }
+
+ if (formState.username.trim().length < 3) {
+ return 'Username must be at least 3 characters long.';
+ }
+
+ if (formState.password.length < 6) {
+ return 'Password must be at least 6 characters long.';
+ }
+
+ if (formState.password !== formState.confirmPassword) {
+ return 'Passwords do not match.';
+ }
+
+ return null;
+}
+
+export default function SetupForm() {
+ const { register } = useAuth();
+
+ const [formState, setFormState] = useState(initialState);
+ const [errorMessage, setErrorMessage] = useState('');
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const updateField = useCallback((field: keyof SetupFormState, value: string) => {
+ setFormState((previous) => ({ ...previous, [field]: value }));
+ }, []);
+
+ const handleSubmit = useCallback(
+ async (event: FormEvent) => {
+ event.preventDefault();
+ setErrorMessage('');
+
+ const validationError = validateSetupForm(formState);
+ if (validationError) {
+ setErrorMessage(validationError);
+ return;
+ }
+
+ setIsSubmitting(true);
+ const result = await register(formState.username.trim(), formState.password);
+ if (!result.success) {
+ setErrorMessage(result.error);
+ }
+ setIsSubmitting(false);
+ },
+ [formState, register],
+ );
+
+ return (
+ }
+ >
+
+ updateField('username', value)}
+ placeholder="Enter your username"
+ isDisabled={isSubmitting}
+ />
+
+ updateField('password', value)}
+ placeholder="Enter your password"
+ isDisabled={isSubmitting}
+ type="password"
+ />
+
+ updateField('confirmPassword', value)}
+ placeholder="Confirm your password"
+ isDisabled={isSubmitting}
+ type="password"
+ />
+
+
+
+
+ {isSubmitting ? 'Setting up...' : 'Create Account'}
+
+
+
+ );
+}
diff --git a/src/components/chat/hooks/useChatComposerState.ts b/src/components/chat/hooks/useChatComposerState.ts
index 950db891..bdba41d0 100644
--- a/src/components/chat/hooks/useChatComposerState.ts
+++ b/src/components/chat/hooks/useChatComposerState.ts
@@ -11,9 +11,7 @@ import type {
} from 'react';
import { useDropzone } from 'react-dropzone';
import { authenticatedFetch } from '../../../utils/api';
-
import { thinkingModes } from '../constants/thinkingModes';
-
import { grantClaudeToolPermission } from '../utils/chatPermissions';
import { safeLocalStorage } from '../utils/chatStorage';
import type {
@@ -21,10 +19,10 @@ import type {
PendingPermissionRequest,
PermissionMode,
} from '../types/types';
-import { useFileMentions } from './useFileMentions';
-import { type SlashCommand, useSlashCommands } from './useSlashCommands';
import type { Project, ProjectSession, SessionProvider } from '../../../types/app';
import { escapeRegExp } from '../utils/chatFormatting';
+import { useFileMentions } from './useFileMentions';
+import { type SlashCommand, useSlashCommands } from './useSlashCommands';
type PendingViewSession = {
sessionId: string | null;
diff --git a/src/components/chat/hooks/useChatProviderState.ts b/src/components/chat/hooks/useChatProviderState.ts
index 8bd1151b..9d48ce3d 100644
--- a/src/components/chat/hooks/useChatProviderState.ts
+++ b/src/components/chat/hooks/useChatProviderState.ts
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { authenticatedFetch } from '../../../utils/api';
import { CLAUDE_MODELS, CODEX_MODELS, CURSOR_MODELS, GEMINI_MODELS } from '../../../../shared/modelConstants';
-import type { PendingPermissionRequest, PermissionMode, Provider } from '../types/types';
+import type { PendingPermissionRequest, PermissionMode } from '../types/types';
import type { ProjectSession, SessionProvider } from '../../../types/app';
interface UseChatProviderStateArgs {
diff --git a/src/components/chat/hooks/useChatSessionState.ts b/src/components/chat/hooks/useChatSessionState.ts
index c81954a5..71c9a6cb 100644
--- a/src/components/chat/hooks/useChatSessionState.ts
+++ b/src/components/chat/hooks/useChatSessionState.ts
@@ -1,6 +1,5 @@
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import type { MutableRefObject } from 'react';
-
import { api, authenticatedFetch } from '../../../utils/api';
import type { ChatMessage, Provider } from '../types/types';
import type { Project, ProjectSession } from '../../../types/app';
diff --git a/src/components/chat/hooks/useFileMentions.tsx b/src/components/chat/hooks/useFileMentions.tsx
index 44450861..c53f4c7b 100644
--- a/src/components/chat/hooks/useFileMentions.tsx
+++ b/src/components/chat/hooks/useFileMentions.tsx
@@ -161,7 +161,7 @@ export function useFileMentions({ selectedProject, input, setInput, textareaRef
fileMentionSet.has(part) ? (
{part}
diff --git a/src/components/chat/tools/README.md b/src/components/chat/tools/README.md
index 206d90fc..50a15cb8 100644
--- a/src/components/chat/tools/README.md
+++ b/src/components/chat/tools/README.md
@@ -17,7 +17,7 @@ tools/
│ ├── CollapsibleDisplay.tsx # Expandable tool display (uses children pattern)
│ ├── CollapsibleSection.tsx # / wrapper
│ ├── ContentRenderers/
-│ │ ├── DiffViewer.tsx # File diff viewer (memoized)
+│ │ ├── ToolDiffViewer.tsx # File diff viewer (memoized)
│ │ ├── MarkdownContent.tsx # Markdown renderer
│ │ ├── FileListContent.tsx # Comma-separated clickable file list
│ │ ├── TodoListContent.tsx # Todo items with status badges
@@ -82,7 +82,7 @@ Wraps `CollapsibleSection` (``/``) with a `border-l-2` accent
rawContent="..." // Raw JSON string
toolCategory="edit" // Drives border color
>
- // Content as children
+ // Content as children
```
@@ -217,7 +217,7 @@ interface ToolDisplayConfig {
- **ToolRenderer** is wrapped with `React.memo` — skips re-render when props haven't changed
- **parsedData** is memoized with `useMemo` — JSON parsing only runs when input changes
-- **DiffViewer** memoizes `createDiff()` — expensive diff computation cached
+- **ToolDiffViewer** memoizes `createDiff()` — expensive diff computation cached
- **MessageComponent** caches `localStorage` reads and timestamp formatting via `useMemo`
- Tool results route through `ToolRenderer` (no duplicate rendering paths)
- `CollapsibleDisplay` uses children pattern (no wasteful contentProps indirection)
diff --git a/src/components/chat/tools/ToolRenderer.tsx b/src/components/chat/tools/ToolRenderer.tsx
index e3c88b0b..1f1159f5 100644
--- a/src/components/chat/tools/ToolRenderer.tsx
+++ b/src/components/chat/tools/ToolRenderer.tsx
@@ -1,8 +1,8 @@
import React, { memo, useMemo, useCallback } from 'react';
-import { getToolConfig } from './configs/toolConfigs';
-import { OneLineDisplay, CollapsibleDisplay, DiffViewer, MarkdownContent, FileListContent, TodoListContent, TaskListContent, TextContent, QuestionAnswerContent, SubagentContainer } from './components';
import type { Project } from '../../../types/app';
import type { SubagentChildTool } from '../types/types';
+import { getToolConfig } from './configs/toolConfigs';
+import { OneLineDisplay, CollapsibleDisplay, ToolDiffViewer, MarkdownContent, FileListContent, TodoListContent, TaskListContent, TextContent, QuestionAnswerContent, SubagentContainer } from './components';
type DiffLine = {
type: string;
@@ -142,7 +142,7 @@ export const ToolRenderer: React.FC = memo(({
case 'diff':
if (createDiff) {
contentComponent = (
- onFileOpen?.(contentProps.filePath)}
@@ -202,7 +202,7 @@ export const ToolRenderer: React.FC = memo(({
const msg = displayConfig.getMessage?.(parsedData) || 'Success';
contentComponent = (
-
+
{msg}
diff --git a/src/components/chat/tools/components/CollapsibleDisplay.tsx b/src/components/chat/tools/components/CollapsibleDisplay.tsx
index dd4e2423..1175893e 100644
--- a/src/components/chat/tools/components/CollapsibleDisplay.tsx
+++ b/src/components/chat/tools/components/CollapsibleDisplay.tsx
@@ -43,7 +43,7 @@ export const CollapsibleDisplay: React.FC = ({
const borderColor = borderColorMap[toolCategory || 'default'] || borderColorMap.default;
return (
-
+
= ({
{children}
{showRawParameters && rawContent && (
-
-
+
+
= ({
raw params
-
+
{rawContent}
diff --git a/src/components/chat/tools/components/CollapsibleSection.tsx b/src/components/chat/tools/components/CollapsibleSection.tsx
index 0d6615d4..c19e8e8e 100644
--- a/src/components/chat/tools/components/CollapsibleSection.tsx
+++ b/src/components/chat/tools/components/CollapsibleSection.tsx
@@ -23,10 +23,10 @@ export const CollapsibleSection: React.FC = ({
className = ''
}) => {
return (
-
-
+
+
= ({
{toolName && (
- {toolName}
+ {toolName}
)}
{toolName && (
- /
+ /
)}
{onTitleClick ? (
{ e.preventDefault(); e.stopPropagation(); onTitleClick(); }}
- className="text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 font-mono hover:underline truncate flex-1 text-left transition-colors"
+ className="flex-1 truncate text-left font-mono text-blue-600 transition-colors hover:text-blue-700 hover:underline dark:text-blue-400 dark:hover:text-blue-300"
>
{title}
) : (
-
+
{title}
)}
- {action && {action} }
+ {action && {action} }
{children}
diff --git a/src/components/chat/tools/components/ContentRenderers/FileListContent.tsx b/src/components/chat/tools/components/ContentRenderers/FileListContent.tsx
index 695b4f2d..035166be 100644
--- a/src/components/chat/tools/components/ContentRenderers/FileListContent.tsx
+++ b/src/components/chat/tools/components/ContentRenderers/FileListContent.tsx
@@ -23,11 +23,11 @@ export const FileListContent: React.FC
= ({
return (
{title && (
-
+
{title}
)}
-
+
{files.map((file, index) => {
const filePath = typeof file === 'string' ? file : file.path;
const fileName = filePath.split('/').pop() || filePath;
@@ -39,13 +39,13 @@ export const FileListContent: React.FC
= ({
{fileName}
{index < files.length - 1 && (
- ,
+ ,
)}
);
diff --git a/src/components/chat/tools/components/ContentRenderers/QuestionAnswerContent.tsx b/src/components/chat/tools/components/ContentRenderers/QuestionAnswerContent.tsx
index 9ff2c2a2..90c43c6d 100644
--- a/src/components/chat/tools/components/ContentRenderers/QuestionAnswerContent.tsx
+++ b/src/components/chat/tools/components/ContentRenderers/QuestionAnswerContent.tsx
@@ -33,31 +33,31 @@ export const QuestionAnswerContent: React.FC = ({
return (
setExpandedIdx(isExpanded ? null : idx)}
- className="w-full text-left px-3 py-2 flex items-start gap-2.5 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"
+ className="flex w-full items-start gap-2.5 px-3 py-2 text-left transition-colors hover:bg-gray-50 dark:hover:bg-gray-800/50"
>
- 0
? 'bg-blue-100 dark:bg-blue-900/40'
: 'bg-gray-100 dark:bg-gray-800'
}`}>
{answerLabels.length > 0 ? (
-
+
) : (
-
+
)}
-
-
+
+
{q.header && (
-
+
{q.header}
)}
@@ -67,22 +67,22 @@ export const QuestionAnswerContent: React.FC = ({
)}
-
+
{q.question}
{!isExpanded && answerLabels.length > 0 && (
-
+
{answerLabels.map((lbl) => {
const isCustom = !q.options.some(o => o.label === lbl);
return (
{lbl}
{isCustom && (
- (custom)
+ (custom)
)}
);
@@ -91,14 +91,14 @@ export const QuestionAnswerContent: React.FC = ({
)}
{!isExpanded && skipped && hasAnyAnswer && (
-
+
Skipped
)}
= ({
{isExpanded && (
-
-
+
+
{q.options.map((opt) => {
const wasSelected = answerLabels.includes(opt.label);
return (
-
-
-
+
+
{opt.label}
{opt.description && (
-
{opt.description}
@@ -151,22 +151,22 @@ export const QuestionAnswerContent: React.FC = ({
{answerLabels.filter(lbl => !q.options.some(o => o.label === lbl)).map(lbl => (
-
-
+
-
-
{lbl}
-
(custom)
+
+ {lbl}
+ (custom)
))}
{skipped && hasAnyAnswer && (
-
+
No answer provided
)}
@@ -178,7 +178,7 @@ export const QuestionAnswerContent: React.FC
= ({
})}
{!hasAnyAnswer && total === 1 && (
-
+
Skipped
)}
diff --git a/src/components/chat/tools/components/ContentRenderers/TaskListContent.tsx b/src/components/chat/tools/components/ContentRenderers/TaskListContent.tsx
index 5ae3f71a..c15fc392 100644
--- a/src/components/chat/tools/components/ContentRenderers/TaskListContent.tsx
+++ b/src/components/chat/tools/components/ContentRenderers/TaskListContent.tsx
@@ -39,7 +39,7 @@ function parseTaskContent(content: string): TaskItem[] {
const statusConfig = {
completed: {
icon: (
-
+
),
@@ -48,7 +48,7 @@ const statusConfig = {
},
in_progress: {
icon: (
-
+
),
@@ -57,7 +57,7 @@ const statusConfig = {
},
pending: {
icon: (
-
+
),
@@ -76,7 +76,7 @@ export const TaskListContent: React.FC = ({ content }) =>
// If we couldn't parse any tasks, fall back to text display
if (tasks.length === 0) {
return (
-
+
{content}
);
@@ -87,13 +87,13 @@ export const TaskListContent: React.FC = ({ content }) =>
return (
-
+
{completed}/{total} completed
-
+
0 ? (completed / total) * 100 : 0}%` }}
/>
@@ -104,16 +104,16 @@ export const TaskListContent: React.FC
= ({ content }) =>
return (
{config.icon}
-
+
#{task.id}
-
+
{task.subject}
-
+
{task.status.replace('_', ' ')}
diff --git a/src/components/chat/tools/components/ContentRenderers/TextContent.tsx b/src/components/chat/tools/components/ContentRenderers/TextContent.tsx
index 811165aa..abe3367e 100644
--- a/src/components/chat/tools/components/ContentRenderers/TextContent.tsx
+++ b/src/components/chat/tools/components/ContentRenderers/TextContent.tsx
@@ -22,10 +22,11 @@ export const TextContent: React.FC = ({
formattedJson = JSON.stringify(parsed, null, 2);
} catch (e) {
// If parsing fails, use original content
+ console.warn('Failed to parse JSON content:', e);
}
return (
-
+
{formattedJson}
);
@@ -33,7 +34,7 @@ export const TextContent: React.FC = ({
if (format === 'code') {
return (
-
+
{content}
);
@@ -41,7 +42,7 @@ export const TextContent: React.FC = ({
// Plain text
return (
-
+
{content}
);
diff --git a/src/components/chat/tools/components/ContentRenderers/TodoList.tsx b/src/components/chat/tools/components/ContentRenderers/TodoList.tsx
new file mode 100644
index 00000000..a9e0a403
--- /dev/null
+++ b/src/components/chat/tools/components/ContentRenderers/TodoList.tsx
@@ -0,0 +1,152 @@
+import { memo, useMemo } from 'react';
+import { CheckCircle2, Circle, Clock, type LucideIcon } from 'lucide-react';
+import { Badge } from '../../../../../shared/view/ui';
+
+type TodoStatus = 'completed' | 'in_progress' | 'pending';
+type TodoPriority = 'high' | 'medium' | 'low';
+
+export type TodoItem = {
+ id?: string;
+ content: string;
+ status: string;
+ priority?: string;
+};
+
+type NormalizedTodoItem = {
+ id?: string;
+ content: string;
+ status: TodoStatus;
+ priority: TodoPriority;
+};
+
+type StatusConfig = {
+ icon: LucideIcon;
+ iconClassName: string;
+ badgeClassName: string;
+ textClassName: string;
+};
+
+// Centralized visual config keeps rendering logic compact and easier to scan.
+const STATUS_CONFIG: Record
= {
+ completed: {
+ icon: CheckCircle2,
+ iconClassName: 'w-3.5 h-3.5 text-green-500 dark:text-green-400',
+ badgeClassName:
+ 'bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-200 border-green-200 dark:border-green-800',
+ textClassName: 'line-through text-gray-500 dark:text-gray-400',
+ },
+ in_progress: {
+ icon: Clock,
+ iconClassName: 'w-3.5 h-3.5 text-blue-500 dark:text-blue-400',
+ badgeClassName:
+ 'bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 border-blue-200 dark:border-blue-800',
+ textClassName: 'text-gray-900 dark:text-gray-100',
+ },
+ pending: {
+ icon: Circle,
+ iconClassName: 'w-3.5 h-3.5 text-gray-400 dark:text-gray-500',
+ badgeClassName:
+ 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-700',
+ textClassName: 'text-gray-900 dark:text-gray-100',
+ },
+};
+
+const PRIORITY_BADGE_CLASS: Record = {
+ high: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 border-red-200 dark:border-red-800',
+ medium:
+ 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-300 border-yellow-200 dark:border-yellow-800',
+ low: 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-700',
+};
+
+// Incoming tool payloads can vary; normalize to supported UI states.
+const normalizeStatus = (status: string): TodoStatus => {
+ if (status === 'completed' || status === 'in_progress') {
+ return status;
+ }
+ return 'pending';
+};
+
+const normalizePriority = (priority?: string): TodoPriority => {
+ if (priority === 'high' || priority === 'medium') {
+ return priority;
+ }
+ return 'low';
+};
+
+const TodoRow = memo(
+ ({ todo }: { todo: NormalizedTodoItem }) => {
+ const statusConfig = STATUS_CONFIG[todo.status];
+ const StatusIcon = statusConfig.icon;
+
+ return (
+
+
+
+
+
+
+
+ {todo.content}
+
+
+
+ {todo.priority}
+
+
+ {todo.status.replace('_', ' ')}
+
+
+
+
+
+ );
+ }
+);
+
+const TodoList = memo(
+ ({
+ todos,
+ isResult = false,
+ }: {
+ todos: TodoItem[];
+ isResult?: boolean;
+ }) => {
+ // Memoize normalization to avoid recomputing list metadata on every render.
+ const normalizedTodos = useMemo(
+ () =>
+ todos.map((todo) => ({
+ id: todo.id,
+ content: todo.content,
+ status: normalizeStatus(todo.status),
+ priority: normalizePriority(todo.priority),
+ })),
+ [todos]
+ );
+
+ if (normalizedTodos.length === 0) {
+ return null;
+ }
+
+ return (
+
+ {isResult && (
+
+ Todo List ({normalizedTodos.length}{' '}
+ {normalizedTodos.length === 1 ? 'item' : 'items'})
+
+ )}
+ {normalizedTodos.map((todo, index) => (
+
+ ))}
+
+ );
+ }
+);
+
+export default TodoList;
diff --git a/src/components/chat/tools/components/ContentRenderers/TodoListContent.tsx b/src/components/chat/tools/components/ContentRenderers/TodoListContent.tsx
index 5d318ba1..decbc9c3 100644
--- a/src/components/chat/tools/components/ContentRenderers/TodoListContent.tsx
+++ b/src/components/chat/tools/components/ContentRenderers/TodoListContent.tsx
@@ -1,23 +1,40 @@
-import React from 'react';
-import TodoList from '../../../../TodoList';
+import { memo, useMemo } from 'react';
+import TodoList, { type TodoItem } from './TodoList';
-interface TodoListContentProps {
- todos: Array<{
- id?: string;
- content: string;
- status: string;
- priority?: string;
- }>;
- isResult?: boolean;
-}
+const isTodoItem = (value: unknown): value is TodoItem => {
+ if (typeof value !== 'object' || value === null) {
+ return false;
+ }
+
+ const todo = value as Record;
+ return typeof todo.content === 'string' && typeof todo.status === 'string';
+};
/**
* Renders a todo list
* Used by: TodoWrite, TodoRead
*/
-export const TodoListContent: React.FC = ({
- todos,
- isResult = false
-}) => {
- return ;
-};
+export const TodoListContent = memo(
+ ({
+ todos,
+ isResult = false,
+ }: {
+ todos: unknown;
+ isResult?: boolean;
+ }) => {
+ const safeTodos = useMemo(() => {
+ if (!Array.isArray(todos)) {
+ return [];
+ }
+
+ // Tool payloads are runtime data; render only validated todo objects.
+ return todos.filter(isTodoItem);
+ }, [todos]);
+
+ if (safeTodos.length === 0) {
+ return null;
+ }
+
+ return ;
+ }
+);
diff --git a/src/components/chat/tools/components/InteractiveRenderers/AskUserQuestionPanel.tsx b/src/components/chat/tools/components/InteractiveRenderers/AskUserQuestionPanel.tsx
index 71868510..5a75390f 100644
--- a/src/components/chat/tools/components/InteractiveRenderers/AskUserQuestionPanel.tsx
+++ b/src/components/chat/tools/components/InteractiveRenderers/AskUserQuestionPanel.tsx
@@ -148,32 +148,32 @@ export const AskUserQuestionPanel: React.FC = ({
tabIndex={-1}
onKeyDown={handleKeyDown}
className={`w-full outline-none transition-all duration-500 ease-out ${
- mounted ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-3'
+ mounted ? 'translate-y-0 opacity-100' : 'translate-y-3 opacity-0'
}`}
>
-
+
{/* Accent line */}
-
+
{/* Header + Question — compact */}
-
-
+
+
{/* Question icon */}
-
-
-
+
+
Claude needs your input
{q.header && (
-
+
{q.header}
)}
@@ -181,7 +181,7 @@ export const AskUserQuestionPanel: React.FC = ({
{/* Step counter */}
{!isSingle && (
-
+
{currentStep + 1}/{total}
)}
@@ -189,7 +189,7 @@ export const AskUserQuestionPanel: React.FC = ({
{/* Progress dots (multi-question) */}
{!isSingle && (
-
+
{questions.map((_, i) => (
= ({
)}
{/* Question text */}
-
+
{q.question}
{multi && (
@@ -217,7 +217,7 @@ export const AskUserQuestionPanel: React.FC = ({
{/* Options — tight spacing */}
-
+
{q.options.map((opt, optIdx) => {
const isSelected = selected.has(opt.label);
@@ -226,25 +226,25 @@ export const AskUserQuestionPanel: React.FC
= ({
key={opt.label}
type="button"
onClick={() => toggleOption(currentStep, opt.label, multi)}
- className={`group w-full text-left flex items-center gap-2.5 px-3 py-2 rounded-lg border transition-all duration-150 ${
+ className={`group flex w-full items-center gap-2.5 rounded-lg border px-3 py-2 text-left transition-all duration-150 ${
isSelected
- ? 'border-blue-300 dark:border-blue-600 bg-blue-50/80 dark:bg-blue-900/25 ring-1 ring-blue-200/50 dark:ring-blue-700/30'
- : 'border-gray-200 dark:border-gray-700/60 hover:border-gray-300 dark:hover:border-gray-600 hover:bg-gray-50/60 dark:hover:bg-gray-750/50'
+ ? 'border-blue-300 bg-blue-50/80 ring-1 ring-blue-200/50 dark:border-blue-600 dark:bg-blue-900/25 dark:ring-blue-700/30'
+ : 'dark:hover:bg-gray-750/50 border-gray-200 hover:border-gray-300 hover:bg-gray-50/60 dark:border-gray-700/60 dark:hover:border-gray-600'
}`}
>
{/* Keyboard hint */}
-
{optIdx + 1}
-
+
{opt.label}
@@ -262,7 +262,7 @@ export const AskUserQuestionPanel: React.FC
= ({
{/* Selection check */}
{isSelected && (
-
+
)}
@@ -274,28 +274,28 @@ export const AskUserQuestionPanel: React.FC = ({
toggleOther(currentStep, multi)}
- className={`group w-full text-left flex items-center gap-2.5 px-3 py-2 rounded-lg border transition-all duration-150 ${
+ className={`group flex w-full items-center gap-2.5 rounded-lg border px-3 py-2 text-left transition-all duration-150 ${
isOtherOn
- ? 'border-blue-300 dark:border-blue-600 bg-blue-50/80 dark:bg-blue-900/25 ring-1 ring-blue-200/50 dark:ring-blue-700/30'
- : 'border-dashed border-gray-200 dark:border-gray-700/60 hover:border-gray-300 dark:hover:border-gray-600 hover:bg-gray-50/60 dark:hover:bg-gray-750/50'
+ ? 'border-blue-300 bg-blue-50/80 ring-1 ring-blue-200/50 dark:border-blue-600 dark:bg-blue-900/25 dark:ring-blue-700/30'
+ : 'dark:hover:bg-gray-750/50 border-dashed border-gray-200 hover:border-gray-300 hover:bg-gray-50/60 dark:border-gray-700/60 dark:hover:border-gray-600'
}`}
>
-
0
Other...
{isOtherOn && (
-
+
)}
@@ -320,9 +320,9 @@ export const AskUserQuestionPanel: React.FC = ({
e.stopPropagation();
}}
placeholder="Type your answer..."
- className="w-full text-[13px] rounded-lg border-0 bg-gray-50 dark:bg-gray-900/60 text-gray-900 dark:text-gray-100 px-3 py-1.5 outline-none ring-1 ring-gray-200 dark:ring-gray-700 focus:ring-2 focus:ring-blue-400 dark:focus:ring-blue-500 placeholder:text-gray-400 dark:placeholder:text-gray-600 transition-shadow duration-200"
+ className="w-full rounded-lg border-0 bg-gray-50 px-3 py-1.5 text-[13px] text-gray-900 outline-none ring-1 ring-gray-200 transition-shadow duration-200 placeholder:text-gray-400 focus:ring-2 focus:ring-blue-400 dark:bg-gray-900/60 dark:text-gray-100 dark:ring-gray-700 dark:placeholder:text-gray-600 dark:focus:ring-blue-500"
/>
-
+
Enter
@@ -332,11 +332,11 @@ export const AskUserQuestionPanel: React.FC
= ({
{/* Footer — compact */}
-
+
{isSingle ? 'Skip' : 'Skip all'}
Esc
@@ -347,9 +347,9 @@ export const AskUserQuestionPanel: React.FC = ({
setCurrentStep(s => s - 1)}
- className="inline-flex items-center gap-0.5 text-[11px] font-medium px-2.5 py-1.5 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700/60 transition-all duration-150"
+ className="inline-flex items-center gap-0.5 rounded-lg px-2.5 py-1.5 text-[11px] font-medium text-gray-600 transition-all duration-150 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700/60"
>
-
+
Back
@@ -361,19 +361,19 @@ export const AskUserQuestionPanel: React.FC = ({
type="button"
onClick={handleSubmit}
disabled={!hasCurrentSelection && !Object.keys(buildAnswers()).length}
- className="inline-flex items-center gap-1 text-[11px] font-semibold px-3.5 py-1.5 rounded-lg bg-gradient-to-r from-blue-600 to-blue-500 dark:from-blue-500 dark:to-blue-600 text-white shadow-sm hover:shadow-md disabled:opacity-30 disabled:cursor-not-allowed disabled:shadow-none transition-all duration-200"
+ className="inline-flex items-center gap-1 rounded-lg bg-gradient-to-r from-blue-600 to-blue-500 px-3.5 py-1.5 text-[11px] font-semibold text-white shadow-sm transition-all duration-200 hover:shadow-md disabled:cursor-not-allowed disabled:opacity-30 disabled:shadow-none dark:from-blue-500 dark:to-blue-600"
>
Submit
- Enter
+ Enter
) : (
setCurrentStep(s => s + 1)}
- className="inline-flex items-center gap-1 text-[11px] font-semibold px-3.5 py-1.5 rounded-lg bg-gradient-to-r from-blue-600 to-blue-500 dark:from-blue-500 dark:to-blue-600 text-white shadow-sm hover:shadow-md transition-all duration-200"
+ className="inline-flex items-center gap-1 rounded-lg bg-gradient-to-r from-blue-600 to-blue-500 px-3.5 py-1.5 text-[11px] font-semibold text-white shadow-sm transition-all duration-200 hover:shadow-md dark:from-blue-500 dark:to-blue-600"
>
Next
- Enter
+ Enter
)}
diff --git a/src/components/chat/tools/components/OneLineDisplay.tsx b/src/components/chat/tools/components/OneLineDisplay.tsx
index a73bd22a..a3fcd421 100644
--- a/src/components/chat/tools/components/OneLineDisplay.tsx
+++ b/src/components/chat/tools/components/OneLineDisplay.tsx
@@ -68,16 +68,16 @@ export const OneLineDisplay: React.FC
= ({
const renderCopyButton = () => (
{copied ? (
-
+
) : (
-
+
)}
@@ -89,15 +89,15 @@ export const OneLineDisplay: React.FC = ({
return (
-
-
+
-
-
-
- $ {value}
+
+
+
+ $ {value}
{action === 'copy' && renderCopyButton()}
@@ -105,7 +105,7 @@ export const OneLineDisplay: React.FC
= ({
{secondary && (
-
+
{secondary}
@@ -118,12 +118,12 @@ export const OneLineDisplay: React.FC = ({
if (action === 'open-file') {
const displayName = value.split('/').pop() || value;
return (
-
-
{label || toolName}
-
/
+
+
{label || toolName}
+
/
{displayName}
@@ -135,23 +135,23 @@ export const OneLineDisplay: React.FC = ({
// Search / jump-to-results style
if (action === 'jump-to-results') {
return (
-
-
{label || toolName}
-
/
-
+
+
{label || toolName}
+
/
+
{value}
{secondary && (
-
+
{secondary}
)}
{toolResult && (
-
+
@@ -162,21 +162,21 @@ export const OneLineDisplay: React.FC = ({
// Default one-line style
return (
-
+
{icon && icon !== 'terminal' && (
{icon}
)}
{!icon && (label || toolName) && (
-
{label || toolName}
+
{label || toolName}
)}
{(icon || label || toolName) && (
-
/
+
/
)}
-
+
{value}
{secondary && (
-
+
{secondary}
)}
diff --git a/src/components/chat/tools/components/SubagentContainer.tsx b/src/components/chat/tools/components/SubagentContainer.tsx
index 5ff8b764..ae8099da 100644
--- a/src/components/chat/tools/components/SubagentContainer.tsx
+++ b/src/components/chat/tools/components/SubagentContainer.tsx
@@ -1,6 +1,6 @@
import React from 'react';
-import { CollapsibleSection } from './CollapsibleSection';
import type { SubagentChildTool } from '../../types/types';
+import { CollapsibleSection } from './CollapsibleSection';
interface SubagentContainerProps {
toolInput: unknown;
@@ -57,7 +57,7 @@ export const SubagentContainer: React.FC = ({
const title = `Subagent / ${subagentType}: ${description}`;
return (
-
+
= ({
>
{/* Prompt/request to the subagent */}
{prompt && (
-
+
{prompt}
)}
{/* Current tool indicator (while running) */}
{currentTool && !isComplete && (
-
-
+
+
Currently:
{currentTool.toolName}
{getCompactToolDisplay(currentTool.toolName, currentTool.toolInput) && (
<>
/
-
+
{getCompactToolDisplay(currentTool.toolName, currentTool.toolInput)}
>
@@ -89,8 +89,8 @@ export const SubagentContainer: React.FC = ({
{/* Completion status */}
{isComplete && (
-
-
+
+
Completed ({childTools.length} {childTools.length === 1 ? 'tool' : 'tools'})
@@ -99,10 +99,10 @@ export const SubagentContainer: React.FC
= ({
{/* Tool history (collapsed) */}
{childTools.length > 0 && (
-
-
+
+
= ({
View tool history ({childTools.length})
-
+
{childTools.map((child, index) => (
- {index + 1}.
+ {index + 1}.
{child.toolName}
{getCompactToolDisplay(child.toolName, child.toolInput) && (
-
+
{getCompactToolDisplay(child.toolName, child.toolInput)}
)}
{child.toolResult?.isError && (
- (error)
+ (error)
)}
))}
@@ -163,11 +163,11 @@ export const SubagentContainer: React.FC
= ({
}
return typeof content === 'string' ? (
-
+
{content}
) : content ? (
-
+
{JSON.stringify(content, null, 2)}
) : null;
diff --git a/src/components/chat/tools/components/DiffViewer.tsx b/src/components/chat/tools/components/ToolDiffViewer.tsx
similarity index 56%
rename from src/components/chat/tools/components/DiffViewer.tsx
rename to src/components/chat/tools/components/ToolDiffViewer.tsx
index 626c784e..8f43a395 100644
--- a/src/components/chat/tools/components/DiffViewer.tsx
+++ b/src/components/chat/tools/components/ToolDiffViewer.tsx
@@ -6,7 +6,7 @@ type DiffLine = {
lineNum: number;
};
-interface DiffViewerProps {
+interface ToolDiffViewerProps {
oldContent: string;
newContent: string;
filePath: string;
@@ -19,7 +19,7 @@ interface DiffViewerProps {
/**
* Compact diff viewer — VS Code-style
*/
-export const DiffViewer: React.FC = ({
+export const ToolDiffViewer: React.FC = ({
oldContent,
newContent,
filePath,
@@ -38,44 +38,44 @@ export const DiffViewer: React.FC = ({
);
return (
-
+
{/* Header */}
-
+
{onFileClick ? (
{filePath}
) : (
-
+
{filePath}
)}
-
+
{badge}
{/* Diff lines */}
-
+
{diffLines.map((diffLine, i) => (
{diffLine.type === 'removed' ? '-' : '+'}
{diffLine.content}
diff --git a/src/components/chat/tools/components/index.ts b/src/components/chat/tools/components/index.ts
index 88dc4e5d..5e419662 100644
--- a/src/components/chat/tools/components/index.ts
+++ b/src/components/chat/tools/components/index.ts
@@ -1,5 +1,5 @@
export { CollapsibleSection } from './CollapsibleSection';
-export { DiffViewer } from './DiffViewer';
+export { ToolDiffViewer } from './ToolDiffViewer';
export { OneLineDisplay } from './OneLineDisplay';
export { CollapsibleDisplay } from './CollapsibleDisplay';
export { SubagentContainer } from './SubagentContainer';
diff --git a/src/components/chat/tools/configs/toolConfigs.ts b/src/components/chat/tools/configs/toolConfigs.ts
index 556334a3..40dd3dbc 100644
--- a/src/components/chat/tools/configs/toolConfigs.ts
+++ b/src/components/chat/tools/configs/toolConfigs.ts
@@ -274,6 +274,7 @@ export const TOOL_CONFIGS: Record = {
}
return { todos, isResult: true };
} catch (e) {
+ console.warn('Failed to parse todo list content:', e);
return { todos: [], isResult: true };
}
}
@@ -514,6 +515,7 @@ export const TOOL_CONFIGS: Record = {
content: parsed.plan?.replace(/\\n/g, '\n') || parsed.plan
};
} catch (e) {
+ console.warn('Failed to parse plan content:', e);
return { content: '' };
}
}
@@ -544,6 +546,7 @@ export const TOOL_CONFIGS: Record = {
content: parsed.plan?.replace(/\\n/g, '\n') || parsed.plan
};
} catch (e) {
+ console.warn('Failed to parse plan content:', e);
return { content: '' };
}
}
diff --git a/src/components/chat/view/ChatInterface.tsx b/src/components/chat/view/ChatInterface.tsx
index 65c9043b..90c1921d 100644
--- a/src/components/chat/view/ChatInterface.tsx
+++ b/src/components/chat/view/ChatInterface.tsx
@@ -1,15 +1,15 @@
import React, { useCallback, useEffect, useRef } from 'react';
-import QuickSettingsPanel from '../../QuickSettingsPanel';
-import { useTasksSettings } from '../../../contexts/TasksSettingsContext';
import { useTranslation } from 'react-i18next';
-import ChatMessagesPane from './subcomponents/ChatMessagesPane';
-import ChatComposer from './subcomponents/ChatComposer';
-import type { ChatInterfaceProps } from '../types/types';
+import { useTasksSettings } from '../../../contexts/TasksSettingsContext';
+import { QuickSettingsPanel } from '../../quick-settings-panel';
+import type { ChatInterfaceProps, Provider } from '../types/types';
import { useChatProviderState } from '../hooks/useChatProviderState';
import { useChatSessionState } from '../hooks/useChatSessionState';
import { useChatRealtimeHandlers } from '../hooks/useChatRealtimeHandlers';
import { useChatComposerState } from '../hooks/useChatComposerState';
-import type { Provider } from '../types/types';
+import ChatMessagesPane from './subcomponents/ChatMessagesPane';
+import ChatComposer from './subcomponents/ChatComposer';
+
type PendingViewSession = {
sessionId: string | null;
@@ -87,7 +87,6 @@ function ChatInterface({
isLoadingMoreMessages,
hasMoreMessages,
totalMessages,
- isSystemSessionChange,
setIsSystemSessionChange,
canAbortSession,
setCanAbortSession,
@@ -259,7 +258,7 @@ function ChatInterface({
: t('messageTypes.claude');
return (
-
+
{t('projectSelection.startChatWithProvider', {
@@ -274,7 +273,7 @@ function ChatInterface({
return (
<>
-
+
-
-
-
+
+
+
{selectedProvider === 'cursor' ? 'Cursor' : selectedProvider === 'codex' ? 'Codex' : selectedProvider === 'gemini' ? 'Gemini' : 'Claude'}
-
+
.
diff --git a/src/components/chat/view/subcomponents/ChatComposer.tsx b/src/components/chat/view/subcomponents/ChatComposer.tsx
index 6ac150db..35bf7548 100644
--- a/src/components/chat/view/subcomponents/ChatComposer.tsx
+++ b/src/components/chat/view/subcomponents/ChatComposer.tsx
@@ -1,9 +1,3 @@
-import CommandMenu from './CommandMenu';
-import ClaudeStatus from './ClaudeStatus';
-import MicButton from '../../../mic-button/view/MicButton';
-import ImageAttachment from './ImageAttachment';
-import PermissionRequestsBanner from './PermissionRequestsBanner';
-import ChatInputControls from './ChatInputControls';
import { useTranslation } from 'react-i18next';
import type {
ChangeEvent,
@@ -17,7 +11,13 @@ import type {
SetStateAction,
TouchEvent,
} from 'react';
+import MicButton from '../../../mic-button/view/MicButton';
import type { PendingPermissionRequest, PermissionMode, Provider } from '../../types/types';
+import CommandMenu from './CommandMenu';
+import ClaudeStatus from './ClaudeStatus';
+import ImageAttachment from './ImageAttachment';
+import PermissionRequestsBanner from './PermissionRequestsBanner';
+import ChatInputControls from './ChatInputControls';
interface MentionableFile {
name: string;
@@ -169,7 +169,7 @@ export default function ChatComposer({
: '';
return (
-
+
{!hasQuestionPanel && (
)}
-
+
- {!hasQuestionPanel &&
) => void} className="relative max-w-4xl mx-auto">
+ {!hasQuestionPanel && ) => void} className="relative mx-auto max-w-4xl">
{isDragActive && (
-
-
-
+
+
+
0 && (
-
+
{attachedImages.map((file, index) => (
0 && (
-
+
{filteredFiles.map((file, index) => (
{
event.preventDefault();
@@ -258,8 +258,8 @@ export default function ChatComposer({
onSelectFile(file);
}}
>
-
{file.name}
-
{file.path}
+
{file.name}
+
{file.path}
))}
@@ -277,13 +277,13 @@ export default function ChatComposer({
-
-
+
+
{renderInputWithMentions(input)}
@@ -302,17 +302,17 @@ export default function ChatComposer({
onInput={onTextareaInput}
placeholder={placeholder}
disabled={isLoading}
- className="chat-input-placeholder block w-full pl-12 pr-20 sm:pr-40 py-1.5 sm:py-4 bg-transparent rounded-2xl focus:outline-none text-foreground placeholder-muted-foreground/50 disabled:opacity-50 resize-none min-h-[50px] sm:min-h-[80px] max-h-[40vh] sm:max-h-[300px] overflow-y-auto text-base leading-6 transition-all duration-200"
+ className="chat-input-placeholder block max-h-[40vh] min-h-[50px] w-full resize-none overflow-y-auto rounded-2xl bg-transparent py-1.5 pl-12 pr-20 text-base leading-6 text-foreground placeholder-muted-foreground/50 transition-all duration-200 focus:outline-none disabled:opacity-50 sm:max-h-[300px] sm:min-h-[80px] sm:py-4 sm:pr-40"
style={{ height: '50px' }}
/>
-
+
-
-
+
+
-
+
diff --git a/src/components/chat/view/subcomponents/ChatInputControls.tsx b/src/components/chat/view/subcomponents/ChatInputControls.tsx
index fe7e245d..1c05c02a 100644
--- a/src/components/chat/view/subcomponents/ChatInputControls.tsx
+++ b/src/components/chat/view/subcomponents/ChatInputControls.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
+import type { PermissionMode, Provider } from '../../types/types';
import ThinkingModeSelector from './ThinkingModeSelector';
import TokenUsagePie from './TokenUsagePie';
-import type { PermissionMode, Provider } from '../../types/types';
interface ChatInputControlsProps {
permissionMode: PermissionMode | string;
@@ -38,24 +38,24 @@ export default function ChatInputControls({
const { t } = useTranslation('chat');
return (
-
+
-
+
{slashCommandsCount > 0 && (
{slashCommandsCount}
@@ -107,11 +107,11 @@ export default function ChatInputControls({
-
+