diff --git a/.vscode/settings.json b/.vscode/settings.json index 8ac4102..3b8da53 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -68,6 +68,7 @@ "Codegee", "Codeium", "commitlint", + "conv", "deepseek", "domhandler", "elisp", diff --git a/index.html b/index.html index e4b55d2..b47ef7e 100644 --- a/index.html +++ b/index.html @@ -1,9 +1,13 @@ + Aide +
diff --git a/package.json b/package.json index de5928e..b18482d 100644 --- a/package.json +++ b/package.json @@ -356,11 +356,11 @@ "@commitlint/cz-commitlint": "^19.5.0", "@hookform/resolvers": "^3.9.0", "@ianvs/prettier-plugin-sort-imports": "^4.3.1", - "@langchain/anthropic": "^0.3.3", - "@langchain/community": "^0.3.5", - "@langchain/core": "0.3.10", - "@langchain/langgraph": "^0.2.14", - "@langchain/openai": "^0.3.7", + "@langchain/anthropic": "^0.3.5", + "@langchain/community": "^0.3.6", + "@langchain/core": "0.3.13", + "@langchain/langgraph": "^0.2.16", + "@langchain/openai": "^0.3.11", "@langchain/textsplitters": "^0.1.0", "@lexical/react": "^0.18.0", "@radix-ui/react-accordion": "^1.2.1", @@ -384,11 +384,11 @@ "@radix-ui/react-tabs": "^1.1.1", "@radix-ui/react-tooltip": "^1.1.3", "@radix-ui/react-visually-hidden": "^1.1.0", - "@tanstack/react-query": "^5.59.11", + "@tanstack/react-query": "^5.59.15", "@tomjs/vite-plugin-vscode": "^3.0.0", "@types/fs-extra": "^11.0.4", "@types/global-agent": "^2.1.3", - "@types/node": "^22.5.1", + "@types/node": "^22.7.7", "@types/react": "npm:types-react@rc", "@types/react-dom": "npm:types-react-dom@rc", "@types/shell-quote": "^1.7.5", @@ -397,8 +397,8 @@ "@types/vscode-webview": "^1.57.5", "@typescript-eslint/eslint-plugin": "^7.17.0", "@typescript-eslint/parser": "^7.17.0", - "@vitejs/plugin-react": "^4.3.2", - "@vscode/vsce": "^3.1.1", + "@vitejs/plugin-react": "^4.3.3", + "@vscode/vsce": "^3.2.0", "@xenova/transformers": "^2.17.2", "apache-arrow": "^17.0.0", "autoprefixer": "^10.4.20", @@ -412,7 +412,7 @@ "comment-json": "^4.2.5", "commitizen": "^4.3.1", "cpy": "10.1.0", - "es-toolkit": "^1.24.0", + "es-toolkit": "^1.25.2", "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-typescript": "^18.0.0", @@ -429,7 +429,7 @@ "execa": "^9.4.0", "find-free-ports": "^3.1.1", "flexsearch": "^0.7.43", - "framer-motion": "^11.11.8", + "framer-motion": "^11.11.9", "fs-extra": "^11.2.0", "glob": "^11.0.0", "global-agent": "^3.0.0", @@ -448,12 +448,12 @@ "minimatch": "^10.0.1", "next-themes": "^0.3.0", "p-limit": "^6.1.0", - "pnpm": "^9.12.1", + "pnpm": "^9.12.2", "postcss": "^8.4.47", "prettier": "^3.3.3", "react": "19.0.0-rc-d6cb4e77-20240911", "react-dom": "19.0.0-rc-d6cb4e77-20240911", - "react-hook-form": "^7.53.0", + "react-hook-form": "^7.53.1", "react-markdown": "^9.0.1", "react-resizable-panels": "^2.1.4", "react-router": "^6.27.0", @@ -472,32 +472,32 @@ "socket.io": "^4.8.0", "socket.io-client": "^4.8.0", "sonner": "^1.5.0", - "tailwind-merge": "^2.5.3", - "tailwindcss": "^3.4.13", + "tailwind-merge": "^2.5.4", + "tailwindcss": "^3.4.14", "tailwindcss-animate": "^1.0.7", "tree-sitter-wasms": "^0.1.12", "tsup": "^8.3.0", "turndown": "^7.2.0", "typescript": "5.4.5", - "undici": "^6.20.0", + "undici": "^6.20.1", "unified": "^11.0.5", "use-immer": "^0.10.0", "use-resize-observer": "^9.1.0", "uuid": "^10.0.0", - "vaul": "^1.0.0", + "vaul": "^1.1.0", "vectordb": "^0.11.0", - "vite": "^5.4.8", + "vite": "^5.4.9", "vite-plugin-pages": "^0.32.3", "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^5.0.1", - "vitest": "^2.1.2", + "vitest": "^2.1.3", "web-tree-sitter": "^0.24.3", "zod": "^3.23.8", - "zustand": "^4.5.5" + "zustand": "^5.0.0" }, "pnpm": { "overrides": { - "@langchain/core": "0.3.10", + "@langchain/core": "0.3.13", "lexical": "^0.18.0", "shiki": "^1.20.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6084b1..14ad201 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: - '@langchain/core': 0.3.10 + '@langchain/core': 0.3.13 lexical: ^0.18.0 shiki: ^1.20.0 @@ -18,37 +18,37 @@ importers: version: 0.1.9 '@commitlint/cli': specifier: ^19.5.0 - version: 19.5.0(@types/node@22.5.1)(typescript@5.4.5) + version: 19.5.0(@types/node@22.7.7)(typescript@5.4.5) '@commitlint/config-conventional': specifier: ^19.5.0 version: 19.5.0 '@commitlint/cz-commitlint': specifier: ^19.5.0 - version: 19.5.0(@types/node@22.5.1)(commitizen@4.3.1(@types/node@22.5.1)(typescript@5.4.5))(inquirer@9.3.4)(typescript@5.4.5) + version: 19.5.0(@types/node@22.7.7)(commitizen@4.3.1(@types/node@22.7.7)(typescript@5.4.5))(inquirer@9.3.4)(typescript@5.4.5) '@hookform/resolvers': specifier: ^3.9.0 - version: 3.9.0(react-hook-form@7.53.0(react@19.0.0-rc-d6cb4e77-20240911)) + version: 3.9.0(react-hook-form@7.53.1(react@19.0.0-rc-d6cb4e77-20240911)) '@ianvs/prettier-plugin-sort-imports': specifier: ^4.3.1 version: 4.3.1(@vue/compiler-sfc@3.4.36)(prettier@3.3.3) '@langchain/anthropic': - specifier: ^0.3.3 - version: 0.3.3(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))) - '@langchain/community': specifier: ^0.3.5 - version: 0.3.5(@langchain/anthropic@0.3.3(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))))(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8)))(@xenova/transformers@2.17.2)(axios@1.7.7)(cheerio@1.0.0)(ignore@6.0.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.67.3(zod@3.23.8))(vectordb@0.11.0(@apache-arrow/ts@14.0.2)(apache-arrow@17.0.0))(ws@8.18.0) + version: 0.3.5(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))) + '@langchain/community': + specifier: ^0.3.6 + version: 0.3.6(@langchain/anthropic@0.3.5(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))))(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8)))(@xenova/transformers@2.17.2)(axios@1.7.7)(cheerio@1.0.0)(ignore@6.0.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.68.1(zod@3.23.8))(vectordb@0.11.0(@apache-arrow/ts@14.0.2)(apache-arrow@17.0.0))(ws@8.18.0) '@langchain/core': - specifier: 0.3.10 - version: 0.3.10(openai@4.67.3(zod@3.23.8)) + specifier: 0.3.13 + version: 0.3.13(openai@4.68.1(zod@3.23.8)) '@langchain/langgraph': - specifier: ^0.2.14 - version: 0.2.14(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))) + specifier: ^0.2.16 + version: 0.2.16(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))) '@langchain/openai': - specifier: ^0.3.7 - version: 0.3.7(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))) + specifier: ^0.3.11 + version: 0.3.11(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))) '@langchain/textsplitters': specifier: ^0.1.0 - version: 0.1.0(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))) + version: 0.1.0(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))) '@lexical/react': specifier: ^0.18.0 version: 0.18.0(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911)(yjs@13.6.18) @@ -116,11 +116,11 @@ importers: specifier: ^1.1.0 version: 1.1.0(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911)(types-react-dom@19.0.0-rc.1)(types-react@19.0.0-rc.1) '@tanstack/react-query': - specifier: ^5.59.11 - version: 5.59.11(react@19.0.0-rc-d6cb4e77-20240911) + specifier: ^5.59.15 + version: 5.59.15(react@19.0.0-rc-d6cb4e77-20240911) '@tomjs/vite-plugin-vscode': specifier: ^3.0.0 - version: 3.0.0(@swc/core@1.7.10)(postcss@8.4.47)(typescript@5.4.5)(vite@5.4.8(@types/node@22.5.1)(less@4.2.0)) + version: 3.0.0(@swc/core@1.7.10)(postcss@8.4.47)(typescript@5.4.5)(vite@5.4.9(@types/node@22.7.7)(less@4.2.0)) '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 @@ -128,8 +128,8 @@ importers: specifier: ^2.1.3 version: 2.1.3 '@types/node': - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^22.7.7 + version: 22.7.7 '@types/react': specifier: npm:types-react@rc version: types-react@19.0.0-rc.1 @@ -155,11 +155,11 @@ importers: specifier: ^7.17.0 version: 7.18.0(eslint@8.57.0)(typescript@5.4.5) '@vitejs/plugin-react': - specifier: ^4.3.2 - version: 4.3.2(vite@5.4.8(@types/node@22.5.1)(less@4.2.0)) + specifier: ^4.3.3 + version: 4.3.3(vite@5.4.9(@types/node@22.7.7)(less@4.2.0)) '@vscode/vsce': - specifier: ^3.1.1 - version: 3.1.1 + specifier: ^3.2.0 + version: 3.2.0 '@xenova/transformers': specifier: ^2.17.2 version: 2.17.2 @@ -171,7 +171,7 @@ importers: version: 10.4.20(postcss@8.4.47) babel-plugin-react-compiler: specifier: latest - version: 0.0.0-experimental-ad3b12a-20241011 + version: 19.0.0-beta-9ee70a1-20241017 chalk: specifier: ^5.3.0 version: 5.3.0 @@ -195,13 +195,13 @@ importers: version: 4.2.5 commitizen: specifier: ^4.3.1 - version: 4.3.1(@types/node@22.5.1)(typescript@5.4.5) + version: 4.3.1(@types/node@22.7.7)(typescript@5.4.5) cpy: specifier: 10.1.0 version: 10.1.0 es-toolkit: - specifier: ^1.24.0 - version: 1.24.0 + specifier: ^1.25.2 + version: 1.25.2 eslint: specifier: ^8.57.0 version: 8.57.0 @@ -231,7 +231,7 @@ importers: version: 5.2.1(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3) eslint-plugin-react-compiler: specifier: latest - version: 0.0.0-experimental-45ae4c3-20241011(eslint@8.57.0) + version: 19.0.0-beta-9ee70a1-20241017(eslint@8.57.0) eslint-plugin-simple-import-sort: specifier: ^12.1.1 version: 12.1.1(eslint@8.57.0) @@ -251,8 +251,8 @@ importers: specifier: ^0.7.43 version: 0.7.43 framer-motion: - specifier: ^11.11.8 - version: 11.11.8(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911) + specifier: ^11.11.9 + version: 11.11.9(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911) fs-extra: specifier: ^11.2.0 version: 11.2.0 @@ -282,10 +282,10 @@ importers: version: 1.0.15 knip: specifier: ^5.33.3 - version: 5.33.3(@types/node@22.5.1)(typescript@5.4.5) + version: 5.33.3(@types/node@22.7.7)(typescript@5.4.5) langchain: specifier: ^0.3.2 - version: 0.3.2(@langchain/anthropic@0.3.3(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))))(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8)))(axios@1.7.7)(cheerio@1.0.0)(openai@4.67.3(zod@3.23.8)) + version: 0.3.2(@langchain/anthropic@0.3.5(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))))(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8)))(axios@1.7.7)(cheerio@1.0.0)(openai@4.68.1(zod@3.23.8)) lexical: specifier: ^0.18.0 version: 0.18.0 @@ -308,8 +308,8 @@ importers: specifier: ^6.1.0 version: 6.1.0 pnpm: - specifier: ^9.12.1 - version: 9.12.1 + specifier: ^9.12.2 + version: 9.12.2 postcss: specifier: ^8.4.47 version: 8.4.47 @@ -323,8 +323,8 @@ importers: specifier: 19.0.0-rc-d6cb4e77-20240911 version: 19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911) react-hook-form: - specifier: ^7.53.0 - version: 7.53.0(react@19.0.0-rc-d6cb4e77-20240911) + specifier: ^7.53.1 + version: 7.53.1(react@19.0.0-rc-d6cb4e77-20240911) react-markdown: specifier: ^9.0.1 version: 9.0.1(react@19.0.0-rc-d6cb4e77-20240911)(types-react@19.0.0-rc.1) @@ -380,14 +380,14 @@ importers: specifier: ^1.5.0 version: 1.5.0(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911) tailwind-merge: - specifier: ^2.5.3 - version: 2.5.3 + specifier: ^2.5.4 + version: 2.5.4 tailwindcss: - specifier: ^3.4.13 - version: 3.4.13 + specifier: ^3.4.14 + version: 3.4.14 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.13) + version: 1.0.7(tailwindcss@3.4.14) tree-sitter-wasms: specifier: ^0.1.12 version: 0.1.12 @@ -401,8 +401,8 @@ importers: specifier: 5.4.5 version: 5.4.5 undici: - specifier: ^6.20.0 - version: 6.20.0 + specifier: ^6.20.1 + version: 6.20.1 unified: specifier: ^11.0.5 version: 11.0.5 @@ -416,26 +416,26 @@ importers: specifier: ^10.0.0 version: 10.0.0 vaul: - specifier: ^1.0.0 - version: 1.0.0(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911)(types-react-dom@19.0.0-rc.1)(types-react@19.0.0-rc.1) + specifier: ^1.1.0 + version: 1.1.0(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911)(types-react-dom@19.0.0-rc.1)(types-react@19.0.0-rc.1) vectordb: specifier: ^0.11.0 version: 0.11.0(@apache-arrow/ts@14.0.2)(apache-arrow@17.0.0) vite: - specifier: ^5.4.8 - version: 5.4.8(@types/node@22.5.1)(less@4.2.0) + specifier: ^5.4.9 + version: 5.4.9(@types/node@22.7.7)(less@4.2.0) vite-plugin-pages: specifier: ^0.32.3 - version: 0.32.3(@vue/compiler-sfc@3.4.36)(react-router@6.27.0(react@19.0.0-rc-d6cb4e77-20240911))(vite@5.4.8(@types/node@22.5.1)(less@4.2.0)) + version: 0.32.3(@vue/compiler-sfc@3.4.36)(react-router@6.27.0(react@19.0.0-rc-d6cb4e77-20240911))(vite@5.4.9(@types/node@22.7.7)(less@4.2.0)) vite-plugin-svgr: specifier: ^4.2.0 - version: 4.2.0(rollup@4.21.1)(typescript@5.4.5)(vite@5.4.8(@types/node@22.5.1)(less@4.2.0)) + version: 4.2.0(rollup@4.21.1)(typescript@5.4.5)(vite@5.4.9(@types/node@22.7.7)(less@4.2.0)) vite-tsconfig-paths: specifier: ^5.0.1 - version: 5.0.1(typescript@5.4.5)(vite@5.4.8(@types/node@22.5.1)(less@4.2.0)) + version: 5.0.1(typescript@5.4.5)(vite@5.4.9(@types/node@22.7.7)(less@4.2.0)) vitest: - specifier: ^2.1.2 - version: 2.1.2(@types/node@22.5.1)(less@4.2.0) + specifier: ^2.1.3 + version: 2.1.3(@types/node@22.7.7)(less@4.2.0) web-tree-sitter: specifier: ^0.24.3 version: 0.24.3 @@ -443,8 +443,8 @@ importers: specifier: ^3.23.8 version: 3.23.8 zustand: - specifier: ^4.5.5 - version: 4.5.5(immer@10.1.1)(react@19.0.0-rc-d6cb4e77-20240911)(types-react@19.0.0-rc.1) + specifier: ^5.0.0 + version: 5.0.0(immer@10.1.1)(react@19.0.0-rc-d6cb4e77-20240911)(types-react@19.0.0-rc.1)(use-sync-external-store@1.2.2(react@19.0.0-rc-d6cb4e77-20240911)) website: dependencies: @@ -463,7 +463,7 @@ importers: version: 1.1.44 '@nolebase/vitepress-plugin-inline-link-preview': specifier: ^2.4.0 - version: 2.4.0(@algolia/client-search@4.24.0)(@types/node@22.5.1)(@types/react@18.3.4)(axios@1.7.7)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5) + version: 2.4.0(@algolia/client-search@4.24.0)(@types/node@22.7.7)(@types/react@18.3.4)(axios@1.7.7)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5) '@unocss/preset-icons': specifier: ^0.61.9 version: 0.61.9 @@ -481,10 +481,10 @@ importers: version: 4.0.0 unocss: specifier: ^0.61.9 - version: 0.61.9(postcss@8.4.47)(rollup@4.21.1)(vite@5.3.5(@types/node@22.5.1)(less@4.2.0)) + version: 0.61.9(postcss@8.4.47)(rollup@4.21.1)(vite@5.3.5(@types/node@22.7.7)(less@4.2.0)) vitepress: specifier: 1.3.2 - version: 1.3.2(@algolia/client-search@4.24.0)(@types/node@22.5.1)(@types/react@18.3.4)(axios@1.7.7)(less@4.2.0)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5) + version: 1.3.2(@algolia/client-search@4.24.0)(@types/node@22.7.7)(@types/react@18.3.4)(axios@1.7.7)(less@4.2.0)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5) zod: specifier: 3.23.8 version: 3.23.8 @@ -1556,14 +1556,14 @@ packages: cpu: [x64] os: [win32] - '@langchain/anthropic@0.3.3': - resolution: {integrity: sha512-OvnSV3Tjhb87n7CxWzIcJqcJEM4qoFDYYt6Rua7glQF/Ud5FBTurlzoMunLPTQeF5GdPiaOwP3nUw6I9gF7ppw==} + '@langchain/anthropic@0.3.5': + resolution: {integrity: sha512-AWlF8mSTcxlDdLD+FD9TYFnVaQSCp4foblCDzUR/Xnhn8IvZSzK+3nbxkdVM4a8LS+7GnxP9ED88ZAUvZSQmQg==} engines: {node: '>=18'} peerDependencies: - '@langchain/core': 0.3.10 + '@langchain/core': 0.3.13 - '@langchain/community@0.3.5': - resolution: {integrity: sha512-zcVzQQJpJaqJsxgr5AaNpI/MHCWRo2kpzrHuxgnVlq0WZ7zp9hZ2PVMfFtXN/0R86UkRCHcTe5/ARfv+BXje9Q==} + '@langchain/community@0.3.6': + resolution: {integrity: sha512-lf7bPlaYrjgXim4yeKrDXTQkfQMshY3Ij2ahy0lS+ckXgNf6GTDucCe7I6FMdD9Uom7EjQTubF+Ez7x7h8zRMw==} engines: {node: '>=18'} peerDependencies: '@arcjet/redact': ^v1.0.0-alpha.23 @@ -1593,7 +1593,7 @@ packages: '@google-cloud/storage': ^6.10.1 || ^7.7.0 '@gradientai/nodejs-sdk': ^1.2.0 '@huggingface/inference': ^2.6.4 - '@langchain/core': 0.3.10 + '@langchain/core': 0.3.13 '@layerup/layerup-security': ^1.5.12 '@libsql/client': ^0.14.0 '@mendable/firecrawl-js': ^0.0.13 @@ -1929,33 +1929,33 @@ packages: youtubei.js: optional: true - '@langchain/core@0.3.10': - resolution: {integrity: sha512-MBGDcNeMLRFsEtfzYrqFpome9M2KI7wa4VcFoHPrjf5cvw1gaEAWiMST0jq42tgV3XmukiueCog6kj9Q/hxw2w==} + '@langchain/core@0.3.13': + resolution: {integrity: sha512-sHDlwyHhgeaYC+wfORrWO7sXxD6/GDtZZ5mqjY48YMwB58cVv8hTs8goR/9EwXapYt8fQi2uXTGUV87bHzvdZQ==} engines: {node: '>=18'} '@langchain/langgraph-checkpoint@0.0.10': resolution: {integrity: sha512-BMfJD5Eg39pM0iJmEv50qJL5dJJI5U2oHuNXixWlQ1BKsvtbSs713+EHc21uuvcJUct1MPiv7RdfvwXycLM/aQ==} engines: {node: '>=18'} peerDependencies: - '@langchain/core': 0.3.10 + '@langchain/core': 0.3.13 - '@langchain/langgraph@0.2.14': - resolution: {integrity: sha512-gvneCZDzYzpt+P6ye7pveiRZtlGKWFKk3XAck31yxSf5D/++lP8s6ocMY1x+UaFEfAYd5Qj6jNPI9aPp9Y75jQ==} + '@langchain/langgraph@0.2.16': + resolution: {integrity: sha512-7QipO2o+F1d5ccpai+Yip77cmS+Etvj6Zee47E+Ll2TkQQBP1acl0UnAbYoN11xIQoJwhezySOIx8jkqwLEKfw==} engines: {node: '>=18'} peerDependencies: - '@langchain/core': 0.3.10 + '@langchain/core': 0.3.13 - '@langchain/openai@0.3.7': - resolution: {integrity: sha512-3Jhyy2uKkymYu1iVK18sG2ASZVg0EQcmtTuEPVnrrFGYJ0EIPufejm6bE1ebOHZRc50kSxQwRFCAGrMatNtUiQ==} + '@langchain/openai@0.3.11': + resolution: {integrity: sha512-mEFbpJ8w8NPArsquUlCwxvZTKNkXxqwzvTEYzv6Jb7gUoBDOZtwLg6AdcngTJ+w5VFh3wxgPy0g3zb9Aw0Qbpw==} engines: {node: '>=18'} peerDependencies: - '@langchain/core': 0.3.10 + '@langchain/core': 0.3.13 '@langchain/textsplitters@0.1.0': resolution: {integrity: sha512-djI4uw9rlkAb5iMhtLED+xJebDdAG935AdP4eRTB02R7OB/act55Bj9wsskhZsvuyQRpO4O1wQOp85s6T6GWmw==} engines: {node: '>=18'} peerDependencies: - '@langchain/core': 0.3.10 + '@langchain/core': 0.3.13 '@lexical/clipboard@0.18.0': resolution: {integrity: sha512-ybc+hx14wj0n2ZjdOkLcZ02MRB3UprXjpLDXlByFIuVcZpUxVcp3NzA0UBPOKXYKvdt0bmgjnAsFWM5OSbwS0w==} @@ -3155,11 +3155,11 @@ packages: '@swc/types@0.1.12': resolution: {integrity: sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==} - '@tanstack/query-core@5.59.10': - resolution: {integrity: sha512-XxvnKeBWqDTHstyjA1qmSD5VS/FZ2g/qYvPMhFM7IZF0JnMqMxtzbiUkiTFaZ4YZo/Q84LS0hZi0UncKJ3vIhg==} + '@tanstack/query-core@5.59.13': + resolution: {integrity: sha512-Oou0bBu/P8+oYjXsJQ11j+gcpLAMpqW42UlokQYEz4dE7+hOtVO9rVuolJKgEccqzvyFzqX4/zZWY+R/v1wVsQ==} - '@tanstack/react-query@5.59.11': - resolution: {integrity: sha512-m5I4+4NHy6p0uzKLiq30EdRGk37CHHjXJsfMT0bty/Z/aO11LgXUgDBCq/xma4eO5RlV95sFVmi2QB9nVaLlxg==} + '@tanstack/react-query@5.59.15': + resolution: {integrity: sha512-QbVlAkTI78wB4Mqgf2RDmgC0AOiJqer2c5k9STOOSXGv1S6ZkY37r/6UpE8DbQ2Du0ohsdoXgFNEyv+4eDoPEw==} peerDependencies: react: ^18 || ^19 @@ -3281,8 +3281,8 @@ packages: '@types/node@20.3.0': resolution: {integrity: sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ==} - '@types/node@22.5.1': - resolution: {integrity: sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==} + '@types/node@22.7.7': + resolution: {integrity: sha512-SRxCrrg9CL/y54aiMCG3edPKdprgMVGDXjA3gB8UmmBW5TcXzRUYAh8EWzTnSJFAd1rgImPELza+A3bJ+qxz8Q==} '@types/pad-left@2.1.1': resolution: {integrity: sha512-Xd22WCRBydkGSApl5Bw0PhAOHKSVjNL3E3AwzKaps96IMraPqy5BvZIsBVK6JLwdybUzjHnuWVwpDd0JjTfHXA==} @@ -3473,8 +3473,8 @@ packages: peerDependencies: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 - '@vitejs/plugin-react@4.3.2': - resolution: {integrity: sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==} + '@vitejs/plugin-react@4.3.3': + resolution: {integrity: sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 @@ -3486,13 +3486,13 @@ packages: vite: ^5.0.0 vue: ^3.2.25 - '@vitest/expect@2.1.2': - resolution: {integrity: sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==} + '@vitest/expect@2.1.3': + resolution: {integrity: sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==} - '@vitest/mocker@2.1.2': - resolution: {integrity: sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==} + '@vitest/mocker@2.1.3': + resolution: {integrity: sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==} peerDependencies: - '@vitest/spy': 2.1.2 + '@vitest/spy': 2.1.3 msw: ^2.3.5 vite: ^5.0.0 peerDependenciesMeta: @@ -3501,20 +3501,20 @@ packages: vite: optional: true - '@vitest/pretty-format@2.1.2': - resolution: {integrity: sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==} + '@vitest/pretty-format@2.1.3': + resolution: {integrity: sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==} - '@vitest/runner@2.1.2': - resolution: {integrity: sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==} + '@vitest/runner@2.1.3': + resolution: {integrity: sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==} - '@vitest/snapshot@2.1.2': - resolution: {integrity: sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==} + '@vitest/snapshot@2.1.3': + resolution: {integrity: sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==} - '@vitest/spy@2.1.2': - resolution: {integrity: sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==} + '@vitest/spy@2.1.3': + resolution: {integrity: sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==} - '@vitest/utils@2.1.2': - resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==} + '@vitest/utils@2.1.3': + resolution: {integrity: sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==} '@vscode/vsce-sign-alpine-arm64@2.0.2': resolution: {integrity: sha512-E80YvqhtZCLUv3YAf9+tIbbqoinWLCO/B3j03yQPbjT3ZIHCliKZlsy1peNc4XNZ5uIb87Jn0HWx/ZbPXviuAQ==} @@ -3564,8 +3564,8 @@ packages: '@vscode/vsce-sign@2.0.4': resolution: {integrity: sha512-0uL32egStKYfy60IqnynAChMTbL0oqpqk0Ew0YHiIb+fayuGZWADuIPHWUcY1GCnAA+VgchOPDMxnc2R3XGWEA==} - '@vscode/vsce@3.1.1': - resolution: {integrity: sha512-N62Ca9ElRPLUUzf7l9CeEBlLrYzFPRQq7huKk4pVW+LjIOSXfFIPudixn5QvZcz+yXDOh15IopI3K2o3y9666Q==} + '@vscode/vsce@3.2.0': + resolution: {integrity: sha512-c/AId5Lp50HTszCBDfXfD/Go2djm6qO/WfedP2Y3BpRP+V+ttr8T0mTvZ8WEyTiBp2EfrYnzYUCx0ocB9mHy4Q==} engines: {node: '>= 20'} hasBin: true @@ -3872,8 +3872,8 @@ packages: b4a@1.6.6: resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} - babel-plugin-react-compiler@0.0.0-experimental-ad3b12a-20241011: - resolution: {integrity: sha512-nKOKInm8musJDa45Q9rCLJ8H0PMw1hSBWjEWoTvm+jRnufOdck2mVG3i8e5r5VRJatP6jdfbfXo/Q9iNUotz8g==} + babel-plugin-react-compiler@19.0.0-beta-9ee70a1-20241017: + resolution: {integrity: sha512-AkSce5YYHcreFtuvzI9xnP2kwoYkub8Go3yrz7cPbbCE6oIhFxESbPWJVgye7yZckXuzEZYO4JSE8tq/U0oVfA==} bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -4776,8 +4776,8 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} - es-toolkit@1.24.0: - resolution: {integrity: sha512-nZM+MRSGhKjCdjvqWEFr5Jns6vxoXtBcsl4/cEsGMgsMx8Z2ato4vBTGMUSIQBZJgEdKyNcgGh42yu9xiuNYtQ==} + es-toolkit@1.25.2: + resolution: {integrity: sha512-zEh2aJUwnlDwashas6JN+oFVN08F2s2qBaEwTo6EOACjO9PdPH4eGRBZC2JP/3SDLeANiMTEtVnOGhoG7GwZcA==} es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} @@ -4941,8 +4941,8 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-react-compiler@0.0.0-experimental-45ae4c3-20241011: - resolution: {integrity: sha512-m+BmeFtVWzrHt87sb5g5jLttHdo9YScPiuiingdEqLYtUv7pdVi6pQgY3nCOI4h09C4wmWS9xzpaVNEgiODOBg==} + eslint-plugin-react-compiler@19.0.0-beta-9ee70a1-20241017: + resolution: {integrity: sha512-GdJHMa9Wqfc/JPiv4WW5JjQsuSISdBo7oM/6IjRO8uxaZncDrKK/RyFqbPvgEiNFzDcX8ZZvR8dgfSGvxh2Qpw==} engines: {node: ^14.17.0 || ^16.0.0 || >= 18.0.0} peerDependencies: eslint: '>=7' @@ -5221,8 +5221,8 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - framer-motion@11.11.8: - resolution: {integrity: sha512-mnGQNEoz99GtFXBBPw+Ag5K4FcfP5XrXxrxHz+iE4Lmg7W3sf2gKmGuvfkZCW/yIfcdv5vJd6KiSPETH1Pw68Q==} + framer-motion@11.11.9: + resolution: {integrity: sha512-XpdZseuCrZehdHGuW22zZt3SF5g6AHJHJi7JwQIigOznW4Jg1n0oGPMJQheMaKLC+0rp5gxUKMRYI6ytd3q4RQ==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 @@ -5992,7 +5992,7 @@ packages: '@langchain/anthropic': '*' '@langchain/aws': '*' '@langchain/cohere': '*' - '@langchain/core': 0.3.10 + '@langchain/core': 0.3.13 '@langchain/google-genai': '*' '@langchain/google-vertexai': '*' '@langchain/groq': '*' @@ -6696,8 +6696,8 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} - openai@4.67.3: - resolution: {integrity: sha512-HT2tZgjLgRqbLQNKmYtjdF/4TQuiBvg1oGvTDhwpSEQzxo6/oM1us8VQ53vBK2BiKvCxFuq6gKGG70qfwrNhKg==} + openai@4.68.1: + resolution: {integrity: sha512-C9XmYRHgra1U1G4GGFNqRHQEjxhoOWbQYR85IibfJ0jpHUhOm4/lARiKaC/h3zThvikwH9Dx/XOKWPNVygIS3g==} hasBin: true peerDependencies: zod: ^3.23.8 @@ -6917,8 +6917,8 @@ packages: platform@1.3.6: resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} - pnpm@9.12.1: - resolution: {integrity: sha512-5aflKkGDoC1ZMQV/eg2/+dXpzjFh4z+miuOSElt5KCqKikcKUd/IoO2GIhRC6y+1cBmwmQ7ST6tRm/DhvFzPxA==} + pnpm@9.12.2: + resolution: {integrity: sha512-InIbOhH4FmGuHsaM4ae4eUJaHKW5kcl1sHSsIgsYfOVscI/l22n0yWLJiUUu7nbIKHf07oD0dM69Ye4TRhtiKA==} engines: {node: '>=18.12'} hasBin: true @@ -7081,8 +7081,8 @@ packages: peerDependencies: react: '>=16.13.1' - react-hook-form@7.53.0: - resolution: {integrity: sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==} + react-hook-form@7.53.1: + resolution: {integrity: sha512-6aiQeBda4zjcuaugWvim9WsGqisoUk+etmFEsSUMm451/Ic8L/UAb7sRtMj3V+Hdzm6mMjU1VhiSzYUZeBm0Vg==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -7730,16 +7730,16 @@ packages: engines: {node: '>=12.17'} hasBin: true - tailwind-merge@2.5.3: - resolution: {integrity: sha512-d9ZolCAIzom1nf/5p4LdD5zvjmgSxY0BGgdSvmXIoMYAiPdAW/dSpP7joCDYFY7r/HkEa2qmPtkgsu0xjQeQtw==} + tailwind-merge@2.5.4: + resolution: {integrity: sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==} tailwindcss-animate@1.0.7: resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss@3.4.13: - resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==} + tailwindcss@3.4.14: + resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==} engines: {node: '>=14.0.0'} hasBin: true @@ -8022,8 +8022,8 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - undici@6.20.0: - resolution: {integrity: sha512-AITZfPuxubm31Sx0vr8bteSalEbs9wQb/BOBi9FPlD9Qpd6HxZ4Q0+hI742jBhkPb4RT2v5MQzaW5VhRVyj+9A==} + undici@6.20.1: + resolution: {integrity: sha512-AjQF1QsmqfJys+LXfGTNum+qw4S88CojRInG/6t31W/1fk6G59s92bnAvGz5Cmur+kQv2SURXEvvudLmbrE8QA==} engines: {node: '>=18.17'} unicorn-magic@0.1.0: @@ -8148,8 +8148,8 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vaul@1.0.0: - resolution: {integrity: sha512-TegfMkwy86RSvSiIVREG6OqgRL7agqRsKYyWYacyVUAdpcIi34QoCOED476Mbf8J5d06e1hygSdvJhehlxEBhQ==} + vaul@1.1.0: + resolution: {integrity: sha512-YhO/bikcauk48hzhMhvIvT+U87cuCbNbKk9fF4Ou5UkI9t2KkBMernmdP37pCzF15hrv55fcny1YhexK8h6GVQ==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 @@ -8171,8 +8171,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite-node@2.1.2: - resolution: {integrity: sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==} + vite-node@2.1.3: + resolution: {integrity: sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -8235,8 +8235,8 @@ packages: terser: optional: true - vite@5.4.8: - resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} + vite@5.4.9: + resolution: {integrity: sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -8278,15 +8278,15 @@ packages: postcss: optional: true - vitest@2.1.2: - resolution: {integrity: sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==} + vitest@2.1.3: + resolution: {integrity: sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.1.2 - '@vitest/ui': 2.1.2 + '@vitest/browser': 2.1.3 + '@vitest/ui': 2.1.3 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -8542,13 +8542,14 @@ packages: zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} - zustand@4.5.5: - resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} - engines: {node: '>=12.7.0'} + zustand@5.0.0: + resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==} + engines: {node: '>=12.20.0'} peerDependencies: - '@types/react': '>=16.8' + '@types/react': '>=18.0.0' immer: '>=9.0.6' - react: '>=16.8' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' peerDependenciesMeta: '@types/react': optional: true @@ -8556,6 +8557,8 @@ packages: optional: true react: optional: true + use-sync-external-store: + optional: true zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -9170,11 +9173,11 @@ snapshots: '@chevrotain/utils@11.0.3': {} - '@commitlint/cli@19.5.0(@types/node@22.5.1)(typescript@5.4.5)': + '@commitlint/cli@19.5.0(@types/node@22.7.7)(typescript@5.4.5)': dependencies: '@commitlint/format': 19.5.0 '@commitlint/lint': 19.5.0 - '@commitlint/load': 19.5.0(@types/node@22.5.1)(typescript@5.4.5) + '@commitlint/load': 19.5.0(@types/node@22.7.7)(typescript@5.4.5) '@commitlint/read': 19.5.0 '@commitlint/types': 19.5.0 tinyexec: 0.3.0 @@ -9193,13 +9196,13 @@ snapshots: '@commitlint/types': 19.5.0 ajv: 8.16.0 - '@commitlint/cz-commitlint@19.5.0(@types/node@22.5.1)(commitizen@4.3.1(@types/node@22.5.1)(typescript@5.4.5))(inquirer@9.3.4)(typescript@5.4.5)': + '@commitlint/cz-commitlint@19.5.0(@types/node@22.7.7)(commitizen@4.3.1(@types/node@22.7.7)(typescript@5.4.5))(inquirer@9.3.4)(typescript@5.4.5)': dependencies: '@commitlint/ensure': 19.5.0 - '@commitlint/load': 19.5.0(@types/node@22.5.1)(typescript@5.4.5) + '@commitlint/load': 19.5.0(@types/node@22.7.7)(typescript@5.4.5) '@commitlint/types': 19.5.0 chalk: 5.3.0 - commitizen: 4.3.1(@types/node@22.5.1)(typescript@5.4.5) + commitizen: 4.3.1(@types/node@22.7.7)(typescript@5.4.5) inquirer: 9.3.4 lodash.isplainobject: 4.0.6 word-wrap: 1.2.5 @@ -9235,7 +9238,7 @@ snapshots: '@commitlint/rules': 19.5.0 '@commitlint/types': 19.5.0 - '@commitlint/load@19.5.0(@types/node@22.5.1)(typescript@5.4.5)': + '@commitlint/load@19.5.0(@types/node@22.7.7)(typescript@5.4.5)': dependencies: '@commitlint/config-validator': 19.5.0 '@commitlint/execute-rule': 19.5.0 @@ -9243,7 +9246,7 @@ snapshots: '@commitlint/types': 19.5.0 chalk: 5.3.0 cosmiconfig: 9.0.0(typescript@5.4.5) - cosmiconfig-typescript-loader: 5.0.0(@types/node@22.5.1)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5) + cosmiconfig-typescript-loader: 5.0.0(@types/node@22.7.7)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -9568,9 +9571,9 @@ snapshots: '@floating-ui/utils@0.2.7': {} - '@hookform/resolvers@3.9.0(react-hook-form@7.53.0(react@19.0.0-rc-d6cb4e77-20240911))': + '@hookform/resolvers@3.9.0(react-hook-form@7.53.1(react@19.0.0-rc-d6cb4e77-20240911))': dependencies: - react-hook-form: 7.53.0(react@19.0.0-rc-d6cb4e77-20240911) + react-hook-form: 7.53.1(react@19.0.0-rc-d6cb4e77-20240911) '@huggingface/jinja@0.2.2': {} @@ -9707,26 +9710,26 @@ snapshots: '@lancedb/vectordb-win32-x64-msvc@0.11.0': optional: true - '@langchain/anthropic@0.3.3(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8)))': + '@langchain/anthropic@0.3.5(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8)))': dependencies: '@anthropic-ai/sdk': 0.27.3 - '@langchain/core': 0.3.10(openai@4.67.3(zod@3.23.8)) + '@langchain/core': 0.3.13(openai@4.68.1(zod@3.23.8)) fast-xml-parser: 4.4.1 zod: 3.23.8 zod-to-json-schema: 3.22.5(zod@3.23.8) transitivePeerDependencies: - encoding - '@langchain/community@0.3.5(@langchain/anthropic@0.3.3(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))))(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8)))(@xenova/transformers@2.17.2)(axios@1.7.7)(cheerio@1.0.0)(ignore@6.0.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.67.3(zod@3.23.8))(vectordb@0.11.0(@apache-arrow/ts@14.0.2)(apache-arrow@17.0.0))(ws@8.18.0)': + '@langchain/community@0.3.6(@langchain/anthropic@0.3.5(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))))(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8)))(@xenova/transformers@2.17.2)(axios@1.7.7)(cheerio@1.0.0)(ignore@6.0.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.68.1(zod@3.23.8))(vectordb@0.11.0(@apache-arrow/ts@14.0.2)(apache-arrow@17.0.0))(ws@8.18.0)': dependencies: - '@langchain/core': 0.3.10(openai@4.67.3(zod@3.23.8)) - '@langchain/openai': 0.3.7(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))) + '@langchain/core': 0.3.13(openai@4.68.1(zod@3.23.8)) + '@langchain/openai': 0.3.11(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))) binary-extensions: 2.2.0 expr-eval: 2.0.2 flat: 5.0.2 js-yaml: 4.1.0 - langchain: 0.3.2(@langchain/anthropic@0.3.3(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))))(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8)))(axios@1.7.7)(cheerio@1.0.0)(openai@4.67.3(zod@3.23.8)) - langsmith: 0.1.58(openai@4.67.3(zod@3.23.8)) + langchain: 0.3.2(@langchain/anthropic@0.3.5(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))))(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8)))(axios@1.7.7)(cheerio@1.0.0)(openai@4.68.1(zod@3.23.8)) + langsmith: 0.1.65(openai@4.68.1(zod@3.23.8)) uuid: 10.0.0 zod: 3.23.8 zod-to-json-schema: 3.22.5(zod@3.23.8) @@ -9753,13 +9756,13 @@ snapshots: - openai - peggy - '@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))': + '@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))': dependencies: ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.15 - langsmith: 0.1.65(openai@4.67.3(zod@3.23.8)) + langsmith: 0.1.65(openai@4.68.1(zod@3.23.8)) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 @@ -9769,32 +9772,32 @@ snapshots: transitivePeerDependencies: - openai - '@langchain/langgraph-checkpoint@0.0.10(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8)))': + '@langchain/langgraph-checkpoint@0.0.10(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8)))': dependencies: - '@langchain/core': 0.3.10(openai@4.67.3(zod@3.23.8)) + '@langchain/core': 0.3.13(openai@4.68.1(zod@3.23.8)) uuid: 10.0.0 - '@langchain/langgraph@0.2.14(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8)))': + '@langchain/langgraph@0.2.16(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8)))': dependencies: - '@langchain/core': 0.3.10(openai@4.67.3(zod@3.23.8)) - '@langchain/langgraph-checkpoint': 0.0.10(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))) + '@langchain/core': 0.3.13(openai@4.68.1(zod@3.23.8)) + '@langchain/langgraph-checkpoint': 0.0.10(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))) double-ended-queue: 2.1.0-0 uuid: 10.0.0 zod: 3.23.8 - '@langchain/openai@0.3.7(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8)))': + '@langchain/openai@0.3.11(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8)))': dependencies: - '@langchain/core': 0.3.10(openai@4.67.3(zod@3.23.8)) + '@langchain/core': 0.3.13(openai@4.68.1(zod@3.23.8)) js-tiktoken: 1.0.15 - openai: 4.67.3(zod@3.23.8) + openai: 4.68.1(zod@3.23.8) zod: 3.23.8 zod-to-json-schema: 3.22.5(zod@3.23.8) transitivePeerDependencies: - encoding - '@langchain/textsplitters@0.1.0(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8)))': + '@langchain/textsplitters@0.1.0(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8)))': dependencies: - '@langchain/core': 0.3.10(openai@4.67.3(zod@3.23.8)) + '@langchain/core': 0.3.13(openai@4.68.1(zod@3.23.8)) js-tiktoken: 1.0.15 '@lexical/clipboard@0.18.0': @@ -9968,11 +9971,11 @@ snapshots: dependencies: markdown-it: 14.1.0 - '@nolebase/ui@2.4.0(@algolia/client-search@4.24.0)(@types/node@22.5.1)(@types/react@18.3.4)(axios@1.7.7)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5)': + '@nolebase/ui@2.4.0(@algolia/client-search@4.24.0)(@types/node@22.7.7)(@types/react@18.3.4)(axios@1.7.7)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5)': dependencies: '@iconify-json/octicon': 1.1.56 less: 4.2.0 - vitepress: 1.3.2(@algolia/client-search@4.24.0)(@types/node@22.5.1)(@types/react@18.3.4)(axios@1.7.7)(less@4.2.0)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5) + vitepress: 1.3.2(@algolia/client-search@4.24.0)(@types/node@22.7.7)(@types/react@18.3.4)(axios@1.7.7)(less@4.2.0)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5) vue: 3.4.36(typescript@5.4.5) transitivePeerDependencies: - '@algolia/client-search' @@ -10002,17 +10005,17 @@ snapshots: - typescript - universal-cookie - '@nolebase/vitepress-plugin-inline-link-preview@2.4.0(@algolia/client-search@4.24.0)(@types/node@22.5.1)(@types/react@18.3.4)(axios@1.7.7)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5)': + '@nolebase/vitepress-plugin-inline-link-preview@2.4.0(@algolia/client-search@4.24.0)(@types/node@22.7.7)(@types/react@18.3.4)(axios@1.7.7)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5)': dependencies: '@iconify-json/icon-park-outline': 1.1.15 '@iconify-json/octicon': 1.1.56 '@iconify-json/svg-spinners': 1.1.2 '@nolebase/markdown-it-element-transform': 2.4.0(markdown-it@14.1.0) - '@nolebase/ui': 2.4.0(@algolia/client-search@4.24.0)(@types/node@22.5.1)(@types/react@18.3.4)(axios@1.7.7)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5) + '@nolebase/ui': 2.4.0(@algolia/client-search@4.24.0)(@types/node@22.7.7)(@types/react@18.3.4)(axios@1.7.7)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5) less: 4.2.0 markdown-it: 14.1.0 markdown-it-attrs: 4.1.6(markdown-it@14.1.0) - vitepress: 1.3.2(@algolia/client-search@4.24.0)(@types/node@22.5.1)(@types/react@18.3.4)(axios@1.7.7)(less@4.2.0)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5) + vitepress: 1.3.2(@algolia/client-search@4.24.0)(@types/node@22.7.7)(@types/react@18.3.4)(axios@1.7.7)(less@4.2.0)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5) transitivePeerDependencies: - '@algolia/client-search' - '@types/node' @@ -11077,16 +11080,16 @@ snapshots: '@swc/counter': 0.1.3 optional: true - '@tanstack/query-core@5.59.10': {} + '@tanstack/query-core@5.59.13': {} - '@tanstack/react-query@5.59.11(react@19.0.0-rc-d6cb4e77-20240911)': + '@tanstack/react-query@5.59.15(react@19.0.0-rc-d6cb4e77-20240911)': dependencies: - '@tanstack/query-core': 5.59.10 + '@tanstack/query-core': 5.59.13 react: 19.0.0-rc-d6cb4e77-20240911 '@tomjs/node@2.2.3': {} - '@tomjs/vite-plugin-vscode@3.0.0(@swc/core@1.7.10)(postcss@8.4.47)(typescript@5.4.5)(vite@5.4.8(@types/node@22.5.1)(less@4.2.0))': + '@tomjs/vite-plugin-vscode@3.0.0(@swc/core@1.7.10)(postcss@8.4.47)(typescript@5.4.5)(vite@5.4.9(@types/node@22.7.7)(less@4.2.0))': dependencies: '@tomjs/node': 2.2.3 dayjs: 1.11.12 @@ -11096,7 +11099,7 @@ snapshots: lodash.merge: 4.6.2 node-html-parser: 6.1.13 tsup: 7.2.0(@swc/core@1.7.10)(postcss@8.4.47)(typescript@5.4.5) - vite: 5.4.8(@types/node@22.5.1)(less@4.2.0) + vite: 5.4.9(@types/node@22.7.7)(less@4.2.0) transitivePeerDependencies: - '@swc/core' - postcss @@ -11135,13 +11138,13 @@ snapshots: '@types/conventional-commits-parser@5.0.0': dependencies: - '@types/node': 22.5.1 + '@types/node': 22.7.7 '@types/cookie@0.4.1': {} '@types/cors@2.8.17': dependencies: - '@types/node': 22.5.1 + '@types/node': 22.7.7 '@types/debug@4.1.12': dependencies: @@ -11162,7 +11165,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 22.5.1 + '@types/node': 22.7.7 '@types/global-agent@2.1.3': {} @@ -11190,7 +11193,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 22.5.1 + '@types/node': 22.7.7 '@types/katex@0.16.7': {} @@ -11213,7 +11216,7 @@ snapshots: '@types/node-fetch@2.6.11': dependencies: - '@types/node': 22.5.1 + '@types/node': 22.7.7 form-data: 4.0.0 '@types/node@18.19.39': @@ -11226,7 +11229,7 @@ snapshots: '@types/node@20.3.0': {} - '@types/node@22.5.1': + '@types/node@22.7.7': dependencies: undici-types: 6.19.8 @@ -11346,13 +11349,13 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@unocss/astro@0.61.9(rollup@4.21.1)(vite@5.3.5(@types/node@22.5.1)(less@4.2.0))': + '@unocss/astro@0.61.9(rollup@4.21.1)(vite@5.3.5(@types/node@22.7.7)(less@4.2.0))': dependencies: '@unocss/core': 0.61.9 '@unocss/reset': 0.61.9 - '@unocss/vite': 0.61.9(rollup@4.21.1)(vite@5.3.5(@types/node@22.5.1)(less@4.2.0)) + '@unocss/vite': 0.61.9(rollup@4.21.1)(vite@5.3.5(@types/node@22.7.7)(less@4.2.0)) optionalDependencies: - vite: 5.3.5(@types/node@22.5.1)(less@4.2.0) + vite: 5.3.5(@types/node@22.7.7)(less@4.2.0) transitivePeerDependencies: - rollup - supports-color @@ -11489,7 +11492,7 @@ snapshots: dependencies: '@unocss/core': 0.61.9 - '@unocss/vite@0.61.9(rollup@4.21.1)(vite@5.3.5(@types/node@22.5.1)(less@4.2.0))': + '@unocss/vite@0.61.9(rollup@4.21.1)(vite@5.3.5(@types/node@22.7.7)(less@4.2.0))': dependencies: '@ampproject/remapping': 2.3.0 '@rollup/pluginutils': 5.1.0(rollup@4.21.1) @@ -11501,64 +11504,64 @@ snapshots: chokidar: 3.6.0 fast-glob: 3.3.2 magic-string: 0.30.11 - vite: 5.3.5(@types/node@22.5.1)(less@4.2.0) + vite: 5.3.5(@types/node@22.7.7)(less@4.2.0) transitivePeerDependencies: - rollup - supports-color - '@vitejs/plugin-react@4.3.2(vite@5.4.8(@types/node@22.5.1)(less@4.2.0))': + '@vitejs/plugin-react@4.3.3(vite@5.4.9(@types/node@22.7.7)(less@4.2.0))': dependencies: '@babel/core': 7.25.2 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.4.8(@types/node@22.5.1)(less@4.2.0) + vite: 5.4.9(@types/node@22.7.7)(less@4.2.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.0.5(vite@5.3.5(@types/node@22.5.1)(less@4.2.0))(vue@3.4.36(typescript@5.4.5))': + '@vitejs/plugin-vue@5.0.5(vite@5.3.5(@types/node@22.7.7)(less@4.2.0))(vue@3.4.36(typescript@5.4.5))': dependencies: - vite: 5.3.5(@types/node@22.5.1)(less@4.2.0) + vite: 5.3.5(@types/node@22.7.7)(less@4.2.0) vue: 3.4.36(typescript@5.4.5) - '@vitest/expect@2.1.2': + '@vitest/expect@2.1.3': dependencies: - '@vitest/spy': 2.1.2 - '@vitest/utils': 2.1.2 + '@vitest/spy': 2.1.3 + '@vitest/utils': 2.1.3 chai: 5.1.1 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.4.8(@types/node@22.5.1)(less@4.2.0))': + '@vitest/mocker@2.1.3(@vitest/spy@2.1.3)(vite@5.4.9(@types/node@22.7.7)(less@4.2.0))': dependencies: - '@vitest/spy': 2.1.2 + '@vitest/spy': 2.1.3 estree-walker: 3.0.3 magic-string: 0.30.11 optionalDependencies: - vite: 5.4.8(@types/node@22.5.1)(less@4.2.0) + vite: 5.4.9(@types/node@22.7.7)(less@4.2.0) - '@vitest/pretty-format@2.1.2': + '@vitest/pretty-format@2.1.3': dependencies: tinyrainbow: 1.2.0 - '@vitest/runner@2.1.2': + '@vitest/runner@2.1.3': dependencies: - '@vitest/utils': 2.1.2 + '@vitest/utils': 2.1.3 pathe: 1.1.2 - '@vitest/snapshot@2.1.2': + '@vitest/snapshot@2.1.3': dependencies: - '@vitest/pretty-format': 2.1.2 + '@vitest/pretty-format': 2.1.3 magic-string: 0.30.11 pathe: 1.1.2 - '@vitest/spy@2.1.2': + '@vitest/spy@2.1.3': dependencies: tinyspy: 3.0.0 - '@vitest/utils@2.1.2': + '@vitest/utils@2.1.3': dependencies: - '@vitest/pretty-format': 2.1.2 + '@vitest/pretty-format': 2.1.3 loupe: 3.1.1 tinyrainbow: 1.2.0 @@ -11601,7 +11604,7 @@ snapshots: '@vscode/vsce-sign-win32-arm64': 2.0.2 '@vscode/vsce-sign-win32-x64': 2.0.2 - '@vscode/vsce@3.1.1': + '@vscode/vsce@3.2.0': dependencies: '@azure/identity': 4.3.0 '@vscode/vsce-sign': 2.0.4 @@ -12000,7 +12003,7 @@ snapshots: b4a@1.6.6: {} - babel-plugin-react-compiler@0.0.0-experimental-ad3b12a-20241011: + babel-plugin-react-compiler@19.0.0-beta-9ee70a1-20241017: dependencies: '@babel/generator': 7.2.0 '@babel/types': 7.25.2 @@ -12185,7 +12188,7 @@ snapshots: parse5: 7.1.2 parse5-htmlparser2-tree-adapter: 7.0.0 parse5-parser-stream: 7.1.2 - undici: 6.20.0 + undici: 6.20.1 whatwg-mimetype: 4.0.0 chevrotain-allstar@0.3.1(chevrotain@11.0.3): @@ -12335,10 +12338,10 @@ snapshots: has-own-prop: 2.0.0 repeat-string: 1.6.1 - commitizen@4.3.1(@types/node@22.5.1)(typescript@5.4.5): + commitizen@4.3.1(@types/node@22.7.7)(typescript@5.4.5): dependencies: cachedir: 2.3.0 - cz-conventional-changelog: 3.3.0(@types/node@22.5.1)(typescript@5.4.5) + cz-conventional-changelog: 3.3.0(@types/node@22.7.7)(typescript@5.4.5) dedent: 0.7.0 detect-indent: 6.1.0 find-node-modules: 2.1.3 @@ -12418,9 +12421,9 @@ snapshots: dependencies: layout-base: 2.0.1 - cosmiconfig-typescript-loader@5.0.0(@types/node@22.5.1)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5): + cosmiconfig-typescript-loader@5.0.0(@types/node@22.7.7)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5): dependencies: - '@types/node': 22.5.1 + '@types/node': 22.7.7 cosmiconfig: 9.0.0(typescript@5.4.5) jiti: 1.21.6 typescript: 5.4.5 @@ -12506,16 +12509,16 @@ snapshots: cytoscape@3.30.2: {} - cz-conventional-changelog@3.3.0(@types/node@22.5.1)(typescript@5.4.5): + cz-conventional-changelog@3.3.0(@types/node@22.7.7)(typescript@5.4.5): dependencies: chalk: 2.4.2 - commitizen: 4.3.1(@types/node@22.5.1)(typescript@5.4.5) + commitizen: 4.3.1(@types/node@22.7.7)(typescript@5.4.5) conventional-commit-types: 3.0.0 lodash.map: 4.6.0 longest: 2.0.1 word-wrap: 1.2.5 optionalDependencies: - '@commitlint/load': 19.5.0(@types/node@22.5.1)(typescript@5.4.5) + '@commitlint/load': 19.5.0(@types/node@22.7.7)(typescript@5.4.5) transitivePeerDependencies: - '@types/node' - typescript @@ -12919,7 +12922,7 @@ snapshots: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.17 - '@types/node': 22.5.1 + '@types/node': 22.7.7 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.4.2 @@ -13062,7 +13065,7 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 - es-toolkit@1.24.0: {} + es-toolkit@1.25.2: {} es6-error@4.1.1: {} @@ -13301,7 +13304,7 @@ snapshots: '@types/eslint': 8.56.10 eslint-config-prettier: 9.1.0(eslint@8.57.0) - eslint-plugin-react-compiler@0.0.0-experimental-45ae4c3-20241011(eslint@8.57.0): + eslint-plugin-react-compiler@19.0.0-beta-9ee70a1-20241017(eslint@8.57.0): dependencies: '@babel/core': 7.25.2 '@babel/parser': 7.25.3 @@ -13639,7 +13642,7 @@ snapshots: fraction.js@4.3.7: {} - framer-motion@11.11.8(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911): + framer-motion@11.11.9(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911): dependencies: tslib: 2.6.2 optionalDependencies: @@ -14463,11 +14466,11 @@ snapshots: khroma@2.1.0: {} - knip@5.33.3(@types/node@22.5.1)(typescript@5.4.5): + knip@5.33.3(@types/node@22.7.7)(typescript@5.4.5): dependencies: '@nodelib/fs.walk': 1.2.8 '@snyk/github-codeowners': 1.1.0 - '@types/node': 22.5.1 + '@types/node': 22.7.7 easy-table: 1.2.0 enhanced-resolve: 5.17.1 fast-glob: 3.3.2 @@ -14486,15 +14489,15 @@ snapshots: kolorist@1.8.0: {} - langchain@0.3.2(@langchain/anthropic@0.3.3(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))))(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8)))(axios@1.7.7)(cheerio@1.0.0)(openai@4.67.3(zod@3.23.8)): + langchain@0.3.2(@langchain/anthropic@0.3.5(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))))(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8)))(axios@1.7.7)(cheerio@1.0.0)(openai@4.68.1(zod@3.23.8)): dependencies: - '@langchain/core': 0.3.10(openai@4.67.3(zod@3.23.8)) - '@langchain/openai': 0.3.7(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))) - '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))) + '@langchain/core': 0.3.13(openai@4.68.1(zod@3.23.8)) + '@langchain/openai': 0.3.11(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))) + '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))) js-tiktoken: 1.0.15 js-yaml: 4.1.0 jsonpointer: 5.0.1 - langsmith: 0.1.58(openai@4.67.3(zod@3.23.8)) + langsmith: 0.1.58(openai@4.68.1(zod@3.23.8)) openapi-types: 12.1.3 p-retry: 4.6.2 uuid: 10.0.0 @@ -14502,7 +14505,7 @@ snapshots: zod: 3.23.8 zod-to-json-schema: 3.22.5(zod@3.23.8) optionalDependencies: - '@langchain/anthropic': 0.3.3(@langchain/core@0.3.10(openai@4.67.3(zod@3.23.8))) + '@langchain/anthropic': 0.3.5(@langchain/core@0.3.13(openai@4.68.1(zod@3.23.8))) axios: 1.7.7 cheerio: 1.0.0 transitivePeerDependencies: @@ -14517,7 +14520,7 @@ snapshots: vscode-languageserver-textdocument: 1.0.11 vscode-uri: 3.0.8 - langsmith@0.1.58(openai@4.67.3(zod@3.23.8)): + langsmith@0.1.58(openai@4.68.1(zod@3.23.8)): dependencies: '@types/uuid': 10.0.0 commander: 10.0.1 @@ -14526,9 +14529,9 @@ snapshots: semver: 7.6.3 uuid: 10.0.0 optionalDependencies: - openai: 4.67.3(zod@3.23.8) + openai: 4.68.1(zod@3.23.8) - langsmith@0.1.65(openai@4.67.3(zod@3.23.8)): + langsmith@0.1.65(openai@4.68.1(zod@3.23.8)): dependencies: '@types/uuid': 10.0.0 commander: 10.0.1 @@ -14537,7 +14540,7 @@ snapshots: semver: 7.6.3 uuid: 10.0.0 optionalDependencies: - openai: 4.67.3(zod@3.23.8) + openai: 4.68.1(zod@3.23.8) language-subtag-registry@0.3.23: {} @@ -15410,7 +15413,7 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@4.67.3(zod@3.23.8): + openai@4.68.1(zod@3.23.8): dependencies: '@types/node': 18.19.39 '@types/node-fetch': 2.6.11 @@ -15619,7 +15622,7 @@ snapshots: platform@1.3.6: {} - pnpm@9.12.1: {} + pnpm@9.12.2: {} points-on-curve@0.2.0: {} @@ -15735,7 +15738,7 @@ snapshots: '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 '@types/long': 4.0.2 - '@types/node': 22.5.1 + '@types/node': 22.7.7 long: 4.0.0 proxy-from-env@1.1.0: {} @@ -15784,7 +15787,7 @@ snapshots: '@babel/runtime': 7.24.7 react: 19.0.0-rc-d6cb4e77-20240911 - react-hook-form@7.53.0(react@19.0.0-rc-d6cb4e77-20240911): + react-hook-form@7.53.1(react@19.0.0-rc-d6cb4e77-20240911): dependencies: react: 19.0.0-rc-d6cb4e77-20240911 @@ -16577,13 +16580,13 @@ snapshots: typical: 7.2.0 wordwrapjs: 5.1.0 - tailwind-merge@2.5.3: {} + tailwind-merge@2.5.4: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.13): + tailwindcss-animate@1.0.7(tailwindcss@3.4.14): dependencies: - tailwindcss: 3.4.13 + tailwindcss: 3.4.14 - tailwindcss@3.4.13: + tailwindcss@3.4.14: dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -16901,7 +16904,7 @@ snapshots: undici-types@6.19.8: {} - undici@6.20.0: {} + undici@6.20.1: {} unicorn-magic@0.1.0: {} @@ -16952,9 +16955,9 @@ snapshots: universalify@2.0.1: {} - unocss@0.61.9(postcss@8.4.47)(rollup@4.21.1)(vite@5.3.5(@types/node@22.5.1)(less@4.2.0)): + unocss@0.61.9(postcss@8.4.47)(rollup@4.21.1)(vite@5.3.5(@types/node@22.7.7)(less@4.2.0)): dependencies: - '@unocss/astro': 0.61.9(rollup@4.21.1)(vite@5.3.5(@types/node@22.5.1)(less@4.2.0)) + '@unocss/astro': 0.61.9(rollup@4.21.1)(vite@5.3.5(@types/node@22.7.7)(less@4.2.0)) '@unocss/cli': 0.61.9(rollup@4.21.1) '@unocss/core': 0.61.9 '@unocss/extractor-arbitrary-variants': 0.61.9 @@ -16973,9 +16976,9 @@ snapshots: '@unocss/transformer-compile-class': 0.61.9 '@unocss/transformer-directives': 0.61.9 '@unocss/transformer-variant-group': 0.61.9 - '@unocss/vite': 0.61.9(rollup@4.21.1)(vite@5.3.5(@types/node@22.5.1)(less@4.2.0)) + '@unocss/vite': 0.61.9(rollup@4.21.1)(vite@5.3.5(@types/node@22.7.7)(less@4.2.0)) optionalDependencies: - vite: 5.3.5(@types/node@22.5.1)(less@4.2.0) + vite: 5.3.5(@types/node@22.7.7)(less@4.2.0) transitivePeerDependencies: - postcss - rollup @@ -17028,6 +17031,7 @@ snapshots: use-sync-external-store@1.2.2(react@19.0.0-rc-d6cb4e77-20240911): dependencies: react: 19.0.0-rc-d6cb4e77-20240911 + optional: true util-deprecate@1.0.2: {} @@ -17039,7 +17043,7 @@ snapshots: vary@1.1.2: {} - vaul@1.0.0(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911)(types-react-dom@19.0.0-rc.1)(types-react@19.0.0-rc.1): + vaul@1.1.0(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911)(types-react-dom@19.0.0-rc.1)(types-react@19.0.0-rc.1): dependencies: '@radix-ui/react-dialog': 1.1.2(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911)(types-react-dom@19.0.0-rc.1)(types-react@19.0.0-rc.1) react: 19.0.0-rc-d6cb4e77-20240911 @@ -17078,12 +17082,12 @@ snapshots: '@types/unist': 3.0.2 vfile-message: 4.0.2 - vite-node@2.1.2(@types/node@22.5.1)(less@4.2.0): + vite-node@2.1.3(@types/node@22.7.7)(less@4.2.0): dependencies: cac: 6.7.14 debug: 4.3.6 pathe: 1.1.2 - vite: 5.4.8(@types/node@22.5.1)(less@4.2.0) + vite: 5.4.9(@types/node@22.7.7)(less@4.2.0) transitivePeerDependencies: - '@types/node' - less @@ -17095,7 +17099,7 @@ snapshots: - supports-color - terser - vite-plugin-pages@0.32.3(@vue/compiler-sfc@3.4.36)(react-router@6.27.0(react@19.0.0-rc-d6cb4e77-20240911))(vite@5.4.8(@types/node@22.5.1)(less@4.2.0)): + vite-plugin-pages@0.32.3(@vue/compiler-sfc@3.4.36)(react-router@6.27.0(react@19.0.0-rc-d6cb4e77-20240911))(vite@5.4.9(@types/node@22.7.7)(less@4.2.0)): dependencies: '@types/debug': 4.1.12 debug: 4.3.6 @@ -17105,7 +17109,7 @@ snapshots: json5: 2.2.3 local-pkg: 0.5.0 picocolors: 1.1.0 - vite: 5.4.8(@types/node@22.5.1)(less@4.2.0) + vite: 5.4.9(@types/node@22.7.7)(less@4.2.0) yaml: 2.5.0 optionalDependencies: '@vue/compiler-sfc': 3.4.36 @@ -17113,56 +17117,56 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-svgr@4.2.0(rollup@4.21.1)(typescript@5.4.5)(vite@5.4.8(@types/node@22.5.1)(less@4.2.0)): + vite-plugin-svgr@4.2.0(rollup@4.21.1)(typescript@5.4.5)(vite@5.4.9(@types/node@22.7.7)(less@4.2.0)): dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.21.1) '@svgr/core': 8.1.0(typescript@5.4.5) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.4.5)) - vite: 5.4.8(@types/node@22.5.1)(less@4.2.0) + vite: 5.4.9(@types/node@22.7.7)(less@4.2.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite-tsconfig-paths@5.0.1(typescript@5.4.5)(vite@5.4.8(@types/node@22.5.1)(less@4.2.0)): + vite-tsconfig-paths@5.0.1(typescript@5.4.5)(vite@5.4.9(@types/node@22.7.7)(less@4.2.0)): dependencies: debug: 4.3.6 globrex: 0.1.2 tsconfck: 3.1.1(typescript@5.4.5) optionalDependencies: - vite: 5.4.8(@types/node@22.5.1)(less@4.2.0) + vite: 5.4.9(@types/node@22.7.7)(less@4.2.0) transitivePeerDependencies: - supports-color - typescript - vite@5.3.5(@types/node@22.5.1)(less@4.2.0): + vite@5.3.5(@types/node@22.7.7)(less@4.2.0): dependencies: esbuild: 0.21.5 postcss: 8.4.47 rollup: 4.19.0 optionalDependencies: - '@types/node': 22.5.1 + '@types/node': 22.7.7 fsevents: 2.3.3 less: 4.2.0 - vite@5.4.8(@types/node@22.5.1)(less@4.2.0): + vite@5.4.9(@types/node@22.7.7)(less@4.2.0): dependencies: esbuild: 0.21.5 postcss: 8.4.47 rollup: 4.21.1 optionalDependencies: - '@types/node': 22.5.1 + '@types/node': 22.7.7 fsevents: 2.3.3 less: 4.2.0 - vitepress@1.3.2(@algolia/client-search@4.24.0)(@types/node@22.5.1)(@types/react@18.3.4)(axios@1.7.7)(less@4.2.0)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5): + vitepress@1.3.2(@algolia/client-search@4.24.0)(@types/node@22.7.7)(@types/react@18.3.4)(axios@1.7.7)(less@4.2.0)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)(typescript@5.4.5): dependencies: '@docsearch/css': 3.6.1 '@docsearch/js': 3.6.1(@algolia/client-search@4.24.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0) '@shikijs/core': 1.10.3 '@shikijs/transformers': 1.10.3 '@types/markdown-it': 14.1.1 - '@vitejs/plugin-vue': 5.0.5(vite@5.3.5(@types/node@22.5.1)(less@4.2.0))(vue@3.4.36(typescript@5.4.5)) + '@vitejs/plugin-vue': 5.0.5(vite@5.3.5(@types/node@22.7.7)(less@4.2.0))(vue@3.4.36(typescript@5.4.5)) '@vue/devtools-api': 7.3.6 '@vue/shared': 3.4.35 '@vueuse/core': 10.11.0(vue@3.4.36(typescript@5.4.5)) @@ -17171,7 +17175,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.0.1 shiki: 1.20.0 - vite: 5.3.5(@types/node@22.5.1)(less@4.2.0) + vite: 5.3.5(@types/node@22.7.7)(less@4.2.0) vue: 3.4.36(typescript@5.4.5) optionalDependencies: postcss: 8.4.47 @@ -17202,15 +17206,15 @@ snapshots: - typescript - universal-cookie - vitest@2.1.2(@types/node@22.5.1)(less@4.2.0): + vitest@2.1.3(@types/node@22.7.7)(less@4.2.0): dependencies: - '@vitest/expect': 2.1.2 - '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.4.8(@types/node@22.5.1)(less@4.2.0)) - '@vitest/pretty-format': 2.1.2 - '@vitest/runner': 2.1.2 - '@vitest/snapshot': 2.1.2 - '@vitest/spy': 2.1.2 - '@vitest/utils': 2.1.2 + '@vitest/expect': 2.1.3 + '@vitest/mocker': 2.1.3(@vitest/spy@2.1.3)(vite@5.4.9(@types/node@22.7.7)(less@4.2.0)) + '@vitest/pretty-format': 2.1.3 + '@vitest/runner': 2.1.3 + '@vitest/snapshot': 2.1.3 + '@vitest/spy': 2.1.3 + '@vitest/utils': 2.1.3 chai: 5.1.1 debug: 4.3.6 magic-string: 0.30.11 @@ -17220,11 +17224,11 @@ snapshots: tinyexec: 0.3.0 tinypool: 1.0.0 tinyrainbow: 1.2.0 - vite: 5.4.8(@types/node@22.5.1)(less@4.2.0) - vite-node: 2.1.2(@types/node@22.5.1)(less@4.2.0) + vite: 5.4.9(@types/node@22.7.7)(less@4.2.0) + vite-node: 2.1.3(@types/node@22.7.7)(less@4.2.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.5.1 + '@types/node': 22.7.7 transitivePeerDependencies: - less - lightningcss @@ -17458,12 +17462,11 @@ snapshots: zod@3.23.8: {} - zustand@4.5.5(immer@10.1.1)(react@19.0.0-rc-d6cb4e77-20240911)(types-react@19.0.0-rc.1): - dependencies: - use-sync-external-store: 1.2.2(react@19.0.0-rc-d6cb4e77-20240911) + zustand@5.0.0(immer@10.1.1)(react@19.0.0-rc-d6cb4e77-20240911)(types-react@19.0.0-rc.1)(use-sync-external-store@1.2.2(react@19.0.0-rc-d6cb4e77-20240911)): optionalDependencies: '@types/react': types-react@19.0.0-rc.1 immer: 10.1.1 react: 19.0.0-rc-d6cb4e77-20240911 + use-sync-external-store: 1.2.2(react@19.0.0-rc-d6cb4e77-20240911) zwitch@2.0.4: {} diff --git a/src/extension/commands/command-manager.ts b/src/extension/commands/command-manager.ts index ba964ea..388719c 100644 --- a/src/extension/commands/command-manager.ts +++ b/src/extension/commands/command-manager.ts @@ -15,10 +15,15 @@ export class CommandManager { ...args: ConstructorParameters ) => BaseCommand ): void { + const startTime = Date.now() try { const command = new CommandClass(this.context, this) this.commands.set(CommandClass.name, command) this.context.subscriptions.push(command.register()) + const endTime = Date.now() + logger.dev.verbose( + `Command ${CommandClass.name} registered in ${endTime - startTime}ms` + ) } catch (e) { logger.error(`Failed to register command: ${CommandClass.name}`, e) } diff --git a/src/extension/file-utils/paths.ts b/src/extension/file-utils/paths.ts index 90af5be..52b7623 100644 --- a/src/extension/file-utils/paths.ts +++ b/src/extension/file-utils/paths.ts @@ -4,8 +4,6 @@ import path from 'path' import { getWorkspaceFolder } from '@extension/utils' import fs from 'fs-extra' -import { VsCodeFS } from './vscode-fs' - const AIDE_DIR = process.env.AIDE_GLOBAL_DIR ?? path.join(os.homedir(), '.aide') export const getExt = (filePath: string): string => @@ -74,21 +72,7 @@ export class AidePaths { } getSessionFilePath = (sessionId: string) => - this.joinAideNamespacePath(false, 'sessions', `${sessionId}.json`) - - getSessionsListPath = async () => { - const filePath = this.joinAideNamespacePath( - false, - 'sessions', - 'sessions.json' - ) - - if (!fs.existsSync(filePath)) { - await VsCodeFS.writeJsonFile(filePath, []) - } - - return filePath - } + this.joinAideNamespacePath(false, 'sessions', `session-${sessionId}.json`) // lancedb getGlobalLanceDbPath = () => this.joinAideGlobalPath(true, 'lancedb') diff --git a/src/extension/registers/index.ts b/src/extension/registers/index.ts index bd8da7d..7050cc5 100644 --- a/src/extension/registers/index.ts +++ b/src/extension/registers/index.ts @@ -15,10 +15,10 @@ export const setupRegisters = async (registerManager: RegisterManager) => { TmpFileActionRegister, AideKeyUsageStatusBarRegister, AutoOpenCorrespondingFilesRegister, - ModelRegister, - CodebaseWatcherRegister, ServerPluginRegister, - WebviewRegister + WebviewRegister, + ModelRegister, + CodebaseWatcherRegister ] satisfies (typeof BaseRegister)[] for await (const Register of Registers) { diff --git a/src/extension/registers/register-manager.ts b/src/extension/registers/register-manager.ts index 2d69ce3..2bf5184 100644 --- a/src/extension/registers/register-manager.ts +++ b/src/extension/registers/register-manager.ts @@ -17,6 +17,7 @@ export class RegisterManager { ...args: ConstructorParameters ) => BaseRegister ): Promise { + const startTime = Date.now() try { const register = new RegisterClass( this.context, @@ -25,6 +26,10 @@ export class RegisterManager { ) await register.register() this.registers.set(RegisterClass.name, register) + const endTime = Date.now() + logger.dev.verbose( + `Register ${RegisterClass.name} setup in ${endTime - startTime}ms` + ) } catch (e) { logger.error('Failed to setup register', e) } diff --git a/src/extension/registers/webview-register.ts b/src/extension/registers/webview-register.ts index 96d1fb4..f6fbb55 100644 --- a/src/extension/registers/webview-register.ts +++ b/src/extension/registers/webview-register.ts @@ -42,7 +42,9 @@ export class AideWebViewProvider { { enableScripts: true, retainContextWhenHidden: true, - localResourceRoots: [this.extensionUri] + localResourceRoots: [ + vscode.Uri.joinPath(this.extensionUri, 'dist/webview') + ] } ) await this.setupWebview(this.webviewPanel) @@ -61,7 +63,9 @@ export class AideWebViewProvider { if ('options' in webview.webview) { webview.webview.options = { enableScripts: true, - localResourceRoots: [this.extensionUri] + localResourceRoots: [ + vscode.Uri.joinPath(this.extensionUri, 'dist/webview') + ] } } webview.webview.html = this.getHtmlForWebview(webview.webview) diff --git a/src/extension/utils.ts b/src/extension/utils.ts index 2518f89..2e0d2ee 100644 --- a/src/extension/utils.ts +++ b/src/extension/utils.ts @@ -213,10 +213,32 @@ export const DEV_SERVER = process.env.VITE_DEV_SERVER_URL export const setupHtml = ( webview: vscode.Webview, context: vscode.ExtensionContext -) => - DEV_SERVER - ? __getWebviewHtml__(DEV_SERVER) - : __getWebviewHtml__(webview, context) +) => { + if (DEV_SERVER) return __getWebviewHtml__(DEV_SERVER) + + const html = __getWebviewHtml__(webview, context) + + const baseUri = vscode.Uri.joinPath( + context.extensionUri, + process.env.VITE_WEBVIEW_DIST || 'dist' + ) + + const injectScriptRegex = + /(\s*)(\/\*\s*inject\s+script\s*\*\/)(\s*<\/script>)/g + const injectScriptContent = ` + window.__assetsPath = filename => { + const baseUrl = document.baseURI; + return baseUrl + (filename.startsWith('/') ? filename.slice(1) : filename); + } + ` + return html + .replace( + /\s+(href|src)="(.+?)"/g, + (_, attr, url) => + ` ${attr}="${webview.asWebviewUri(vscode.Uri.joinPath(baseUri, url))}"` + ) + .replace(injectScriptRegex, `$1${injectScriptContent}$3`) +} /** * for inject state into index.html string diff --git a/src/extension/webview-api/controllers/chat-session.controller.ts b/src/extension/webview-api/controllers/chat-session.controller.ts new file mode 100644 index 0000000..65ddd5e --- /dev/null +++ b/src/extension/webview-api/controllers/chat-session.controller.ts @@ -0,0 +1,115 @@ +import { aidePaths } from '@extension/file-utils/paths' +import { VsCodeFS } from '@extension/file-utils/vscode-fs' +import { logger } from '@extension/logger' +import type { ChatContext, Conversation } from '@shared/types/chat-context' +import { convertChatContextToChatSession } from '@shared/utils/convert-chat-context-to-chat-session' + +import { ChatSession, chatSessionsDB } from '../lowdb/chat-sessions-db' +import { Controller } from '../types' + +export class ChatSessionController extends Controller { + readonly name = 'chatSession' + + private getSessionFilePath(sessionId: string): string { + return aidePaths.getSessionFilePath(sessionId) + } + + async createSession(req: { chatContext: ChatContext }): Promise { + const chatSession = convertChatContextToChatSession(req.chatContext) + const now = new Date().getTime() + const session = await chatSessionsDB.add({ + ...chatSession, + createdAt: now, + updatedAt: now + }) + + await VsCodeFS.writeJsonFile(this.getSessionFilePath(session.id), { + ...req.chatContext, + createdAt: now, + updatedAt: now + }) + + return session + } + + async getChatContext(req: { + sessionId: string + }): Promise { + const filePath = this.getSessionFilePath(req.sessionId) + try { + return await VsCodeFS.readJsonFile(filePath) + } catch (error) { + logger.error(`Failed to read session file: ${filePath}`, error) + return null + } + } + + async updateSession(req: { chatContext: ChatContext }): Promise { + const now = new Date().getTime() + const session = await chatSessionsDB.update(req.chatContext.id, { + ...convertChatContextToChatSession(req.chatContext), + updatedAt: now + }) + + if (session) { + await VsCodeFS.writeJsonFile(this.getSessionFilePath(session.id), { + ...req.chatContext, + updatedAt: now + }) + } + } + + async createOrUpdateSession(req: { + chatContext: ChatContext + }): Promise { + const now = new Date().getTime() + const session = await chatSessionsDB.createOrUpdate({ + ...convertChatContextToChatSession(req.chatContext), + updatedAt: now + }) + + await VsCodeFS.writeJsonFile(this.getSessionFilePath(session.id), { + ...req.chatContext, + updatedAt: now + }) + } + + async deleteSession(req: { sessionId: string }): Promise { + await chatSessionsDB.remove(req.sessionId) + await VsCodeFS.unlink(this.getSessionFilePath(req.sessionId)) + } + + async getAllSessions(): Promise { + return await chatSessionsDB.getAll() + } + + async searchSessions(req: { query: string }): Promise { + const sessions = await chatSessionsDB.search(req.query) + const results: ChatSession[] = [] + + for (const session of sessions) { + const chatContext = await this.getChatContext({ sessionId: session.id }) + if ( + chatContext && + this.searchInConversations(chatContext.conversations, req.query) + ) { + results.push(session) + } + } + + return results + } + + private searchInConversations( + conversations: Conversation[], + query: string + ): boolean { + return conversations.some(conv => + conv.contents.some( + content => + content.type === 'text' && + content.text.toLowerCase().includes(query.toLowerCase()) + ) + ) + } +} diff --git a/src/extension/webview-api/index.ts b/src/extension/webview-api/index.ts index 8cd7921..010d2ce 100644 --- a/src/extension/webview-api/index.ts +++ b/src/extension/webview-api/index.ts @@ -5,6 +5,7 @@ import findFreePorts from 'find-free-ports' import { Server } from 'socket.io' import * as vscode from 'vscode' +import { ChatSessionController } from './controllers/chat-session.controller' import { ChatController } from './controllers/chat.controller' import { CodebaseController } from './controllers/codebase.controller' import { DocController } from './controllers/doc.controller' @@ -123,7 +124,8 @@ export const controllers = [ FileController, GitController, SystemController, - DocController + DocController, + ChatSessionController ] as const export type Controllers = typeof controllers diff --git a/src/extension/webview-api/lowdb/base-db.ts b/src/extension/webview-api/lowdb/base-db.ts index c958fd9..af2dd44 100644 --- a/src/extension/webview-api/lowdb/base-db.ts +++ b/src/extension/webview-api/lowdb/base-db.ts @@ -24,9 +24,9 @@ export class BaseDB { return this.db.data.items } - async add(item: Omit): Promise { + async add(item: Omit & { id?: string }): Promise { await this.load() - const newItem = { ...item, id: uuidv4() } as T + const newItem = { ...item, id: item.id || uuidv4() } as T this.db.data.items.push(newItem) await this.db.write() return newItem @@ -48,4 +48,13 @@ export class BaseDB { } return null } + + async createOrUpdate(item: T): Promise { + await this.load() + const existingItem = this.db.data.items.find(i => i.id === item.id) + if (existingItem) { + return (await this.update(item.id, item)) as T + } + return this.add(item) + } } diff --git a/src/extension/webview-api/lowdb/chat-sessions-db.ts b/src/extension/webview-api/lowdb/chat-sessions-db.ts new file mode 100644 index 0000000..4f3df80 --- /dev/null +++ b/src/extension/webview-api/lowdb/chat-sessions-db.ts @@ -0,0 +1,33 @@ +import path from 'path' +import { aidePaths } from '@extension/file-utils/paths' +import { ChatContextType } from '@shared/types/chat-context' + +import { BaseDB, BaseItem } from './base-db' + +export interface ChatSession extends BaseItem { + type: ChatContextType + createdAt: number + updatedAt: number + title: string +} + +class ChatSessionsDB extends BaseDB { + constructor() { + super(path.join(aidePaths.getWorkspaceLowdbPath(), 'sessions.json')) + } + + async add( + item: Omit & { id?: string } + ): Promise { + return super.add(item) + } + + async search(query: string): Promise { + const sessions = await this.getAll() + return sessions.filter(session => + session.title.toLowerCase().includes(query.toLowerCase()) + ) + } +} + +export const chatSessionsDB = new ChatSessionsDB() diff --git a/src/extension/webview-api/lowdb/doc-sites-db.ts b/src/extension/webview-api/lowdb/doc-sites-db.ts index 1b8e303..6b05f81 100644 --- a/src/extension/webview-api/lowdb/doc-sites-db.ts +++ b/src/extension/webview-api/lowdb/doc-sites-db.ts @@ -16,7 +16,9 @@ class DocSitesDB extends BaseDB { } async add( - item: Omit + item: Omit & { + id?: string + } ): Promise { return super.add({ ...item, diff --git a/src/shared/plugins/base/client/client-plugin-registry.ts b/src/shared/plugins/base/client/client-plugin-registry.ts index 6f5dd8a..f915b41 100644 --- a/src/shared/plugins/base/client/client-plugin-registry.ts +++ b/src/shared/plugins/base/client/client-plugin-registry.ts @@ -43,7 +43,7 @@ export class ClientPluginRegistry { | PluginState | ((draft: PluginState) => ValidRecipeReturnType) ) => { - const currentState = options.getState(pluginId) + const currentState = this.getState(pluginId) const newState = typeof updater === 'function' ? produce(currentState, updater) : updater options.setState(pluginId, newState) diff --git a/src/shared/utils/convert-chat-context-to-chat-session.ts b/src/shared/utils/convert-chat-context-to-chat-session.ts new file mode 100644 index 0000000..3a0ee2a --- /dev/null +++ b/src/shared/utils/convert-chat-context-to-chat-session.ts @@ -0,0 +1,14 @@ +import { type ChatContext } from '@shared/types/chat-context' +import type { ChatSession } from '@webview/types/chat' + +import { getTitleFromConversations } from './get-title-from-conversations' + +export const convertChatContextToChatSession = ( + chatContext: ChatContext +): ChatSession => ({ + id: chatContext.id, + title: getTitleFromConversations(chatContext.conversations), + type: chatContext.type, + createdAt: chatContext.createdAt, + updatedAt: chatContext.updatedAt +}) diff --git a/src/shared/utils/get-title-from-conversations.ts b/src/shared/utils/get-title-from-conversations.ts new file mode 100644 index 0000000..8d0e587 --- /dev/null +++ b/src/shared/utils/get-title-from-conversations.ts @@ -0,0 +1,20 @@ +import type { Conversation } from '@shared/types/chat-context' + +export const getTitleFromConversations = ( + conversations: Conversation[], + defaultTitle = 'New Chat' +) => { + let firstHumanMessageText = '' + + conversations + .filter(conversation => conversation.role === 'human') + .forEach(conversation => + conversation.contents.forEach(content => { + if (content.type === 'text' && !firstHumanMessageText) { + firstHumanMessageText = content.text + } + }) + ) + + return firstHumanMessageText || defaultTitle +} diff --git a/src/webview/components/chat/chat-ui.tsx b/src/webview/components/chat/chat-ui.tsx index 192018f..baa3396 100644 --- a/src/webview/components/chat/chat-ui.tsx +++ b/src/webview/components/chat/chat-ui.tsx @@ -1,8 +1,11 @@ import { useRef, type FC } from 'react' -import { GearIcon, PlusIcon } from '@radix-ui/react-icons' +import { GearIcon, MagnifyingGlassIcon, PlusIcon } from '@radix-ui/react-icons' +import { useChatContext, withChatContext } from '@webview/contexts/chat-context' +import { useGlobalSearch } from '@webview/contexts/global-search-context' import { useChatState } from '@webview/hooks/chat/use-chat-state' import { api } from '@webview/services/api-client' import type { Conversation } from '@webview/types/chat' +import { logger } from '@webview/utils/logger' import { useNavigate } from 'react-router' import { ButtonWithTooltip } from '../button-with-tooltip' @@ -12,23 +15,28 @@ import { ChatInput, type ChatInputRef } from './editor/chat-input' import { ChatMessages } from './messages/chat-messages' import { ChatSidebar } from './sidebar/chat-sidebar' -export const ChatUI: FC = () => { +const _ChatUI: FC = () => { const navigate = useNavigate() const { context, setContext, + getContext, + saveSession, + createAndSwitchToNewSession + } = useChatContext() + const { newConversation, setNewConversation, resetNewConversation, historiesConversationsWithUIState, newConversationUIState, - replaceConversationAndTruncate, - setUIStateForSending, - resetUIStateAfterSending, - setConversationEditMode + replaceConversationAndTrimHistory, + prepareUIForSending, + resetUIAfterSending, + toggleConversationEditMode } = useChatState() - const chatInputRef = useRef(null) + const { openSearch } = useGlobalSearch() const resetNewConversationInput = () => { resetNewConversation() @@ -37,22 +45,23 @@ export const ChatUI: FC = () => { const handleSend = async (conversation: Conversation) => { try { - const updatedContext = await replaceConversationAndTruncate(conversation) - setUIStateForSending(conversation.id) - + replaceConversationAndTrimHistory(conversation) + prepareUIForSending(conversation.id) + await saveSession() await api.chat.streamChat( { - chatContext: updatedContext + chatContext: getContext() }, (conversations: Conversation[]) => { - console.log('Received conversations:', conversations) + logger.verbose('Received conversations:', conversations) setContext(draft => { draft.conversations = conversations }) } ) - setConversationEditMode(conversation.id, false) + await saveSession() + toggleConversationEditMode(conversation.id, false) if (conversation.id === newConversation.id) { resetNewConversationInput() @@ -60,7 +69,7 @@ export const ChatUI: FC = () => { chatInputRef.current?.focusOnEditor() } finally { - resetUIStateAfterSending(conversation.id) + resetUIAfterSending(conversation.id) } } @@ -68,7 +77,7 @@ export const ChatUI: FC = () => { isEditMode: boolean, conversation: Conversation ) => { - setConversationEditMode(conversation.id, isEditMode) + toggleConversationEditMode(conversation.id, isEditMode) } return ( @@ -77,12 +86,23 @@ export const ChatUI: FC = () => { sidebar={} headerLeft={ <> + + + @@ -129,3 +149,5 @@ export const ChatUI: FC = () => { ) } + +export const ChatUI = withChatContext(_ChatUI) diff --git a/src/webview/components/chat/editor/chat-input.tsx b/src/webview/components/chat/editor/chat-input.tsx index abf7f0c..f32949a 100644 --- a/src/webview/components/chat/editor/chat-input.tsx +++ b/src/webview/components/chat/editor/chat-input.tsx @@ -68,9 +68,8 @@ const _ChatInput: FC = ({ onSend }) => { const editorRef = useRef(null) - const { pluginRegistry, isPluginRegistryLoaded } = usePluginRegistry() - const { getSelectedFiles, setSelectedFiles } = - usePluginFilesSelectorProviders() + const { pluginRegistryRef, isPluginRegistryLoaded } = usePluginRegistry() + const { selectedFiles, setSelectedFiles } = usePluginFilesSelectorProviders() const mentionOptions = useMentionOptions() const [conversation, setConversation, applyConversation] = useCloneState( @@ -81,7 +80,7 @@ const _ChatInput: FC = ({ // sync conversation plugin states with plugin registry useEffect(() => { Object.entries(conversation.pluginStates).forEach(([pluginId, state]) => { - pluginRegistry?.setState(pluginId as PluginId, state) + pluginRegistryRef.current?.setState(pluginId as PluginId, state) }) }, [isPluginRegistryLoaded, conversation.pluginStates]) @@ -101,7 +100,8 @@ const _ChatInput: FC = ({ updatePluginStatesFromEditorState(editorState, mentionOptions) setConversation(draft => { - draft.pluginStates = pluginRegistry?.providerManagers.state.getAll() || {} + draft.pluginStates = + pluginRegistryRef.current?.providerManagers.state.getAll() || {} }) } @@ -140,8 +140,6 @@ const _ChatInput: FC = ({ handleSend() } - const selectedFiles = getSelectedFiles?.() || [] - const handleSelectedFiles = (files: FileInfo[]) => { setSelectedFiles?.(files) } diff --git a/src/webview/components/chat/messages/roles/chat-human-message.tsx b/src/webview/components/chat/messages/roles/chat-human-message.tsx index 16123ba..2e17147 100644 --- a/src/webview/components/chat/messages/roles/chat-human-message.tsx +++ b/src/webview/components/chat/messages/roles/chat-human-message.tsx @@ -29,7 +29,7 @@ export const ChatHumanMessage: FC = props => { onEditModeChange, context, setContext, - conversation: _conversation, + conversation: initialConversation, onSend, className, style @@ -38,7 +38,7 @@ export const ChatHumanMessage: FC = props => { const { conversation, setConversation } = useConversation( 'human', - _conversation + initialConversation ) const handleSend = () => { diff --git a/src/webview/components/chat/selectors/file-selector/file-list-view.tsx b/src/webview/components/chat/selectors/file-selector/file-list-view.tsx index 23e6593..8cb0e4f 100644 --- a/src/webview/components/chat/selectors/file-selector/file-list-view.tsx +++ b/src/webview/components/chat/selectors/file-selector/file-list-view.tsx @@ -8,11 +8,9 @@ import { TruncateStart } from '@webview/components/truncate-start' import { Command, CommandEmpty, - CommandGroup, CommandItem, CommandList } from '@webview/components/ui/command' -import { useKeyboardNavigation } from '@webview/hooks/use-keyboard-navigation' import { FileInfo } from '@webview/types/chat' import { cn } from '@webview/utils/common' import { getFileNameFromPath } from '@webview/utils/path' @@ -34,20 +32,23 @@ export const FileListView: React.FC = ({ selectedFiles, onSelect }) => { - const listRef = useRef(null) - const itemRefs = useRef<(HTMLDivElement | null)[]>([]) + const commandRef = useRef(null) - // eslint-disable-next-line react-compiler/react-compiler - const { focusedIndex, handleKeyDown } = useKeyboardNavigation({ - listRef, - itemCount: filteredFiles.length, - itemRefs, - onEnter: el => el?.click() + useEvent('keydown', e => { + if (commandRef.current && !commandRef.current.contains(e.target as Node)) { + const event = new KeyboardEvent('keydown', { + key: e.key, + code: e.code, + which: e.which, + keyCode: e.keyCode, + bubbles: true, + cancelable: true + }) + commandRef.current.dispatchEvent(event) + } }) - useEvent('keydown', handleKeyDown) - - const renderItem = (file: FileInfo, index: number) => { + const renderItem = (file: FileInfo) => { const isSelected = selectedFiles.some(f => f.fullPath === file.fullPath) const fileName = getFileNameFromPath(file.relativePath) @@ -55,17 +56,13 @@ export const FileListView: React.FC = ({ return ( onSelect(file)} - ref={el => { - if (itemRefs.current) { - itemRefs.current[index] = el - } + defaultValue={file.fullPath} + value={file.fullPath} + onSelect={() => { + onSelect(file) }} className={cn( - 'cursor-pointer text-sm px-1 py-1 flex items-center hover:bg-secondary', - focusedIndex === index && 'bg-secondary' + 'cursor-pointer text-sm mx-2 px-1 py-1 flex items-center data-[selected=true]:bg-secondary data-[selected=true]:text-foreground' )} >
@@ -86,13 +83,13 @@ export const FileListView: React.FC = ({ return (
- - - No files found. - - {/* eslint-disable-next-line react-compiler/react-compiler */} - {filteredFiles.map((file, index) => renderItem(file, index))} - + + + {!filteredFiles.length ? ( + No files found. + ) : ( + filteredFiles.map(file => renderItem(file)) + )} diff --git a/src/webview/components/chat/selectors/mention-selector/mention-selector.tsx b/src/webview/components/chat/selectors/mention-selector/mention-selector.tsx index 82f827a..de6cc03 100644 --- a/src/webview/components/chat/selectors/mention-selector/mention-selector.tsx +++ b/src/webview/components/chat/selectors/mention-selector/mention-selector.tsx @@ -16,7 +16,6 @@ import { } from '@webview/components/ui/popover' import { useFilteredMentionOptions } from '@webview/hooks/chat/use-filtered-mention-options' import { useControllableState } from '@webview/hooks/use-controllable-state' -import { useKeyboardNavigation } from '@webview/hooks/use-keyboard-navigation' import { MentionOption } from '@webview/types/chat' import { cn } from '@webview/utils/common' import { useEvent } from 'react-use' @@ -63,26 +62,19 @@ export const MentionSelector: React.FC = ({ maxItemLength }) - const itemRefs = useRef<(HTMLDivElement | null)[]>([]) - const { focusedIndex, setFocusedIndex, handleKeyDown, listEventHandlers } = - useKeyboardNavigation({ - itemCount: filteredOptions.length, - itemRefs, - onEnter: el => el?.click() - }) - - useEvent('keydown', handleKeyDown) - - const focusedOption = filteredOptions[focusedIndex] + const [focusedOptionId, setFocusedOptionId] = useState(null) + const focusedOption = filteredOptions.find( + option => option.id === focusedOptionId + ) useEffect(() => { - setFocusedIndex(0) + setFocusedOptionId(filteredOptions?.[0]?.id || null) }, [filteredOptions]) useEffect(() => { if (!isOpen) { setOptionsStack([mentionOptions]) - setFocusedIndex(0) + setFocusedOptionId(filteredOptions?.[0]?.id || null) } }, [isOpen, mentionOptions]) @@ -116,6 +108,20 @@ export const MentionSelector: React.FC = ({ } } + useEvent('keydown', e => { + if (isOpen && commandRef.current) { + const event = new KeyboardEvent('keydown', { + key: e.key, + code: e.code, + which: e.which, + keyCode: e.keyCode, + bubbles: true, + cancelable: true + }) + commandRef.current.dispatchEvent(event) + } + }) + return ( {children} @@ -151,27 +157,30 @@ export const MentionSelector: React.FC = ({ - - + { + const target = filteredOptions.find(item => item.id === val) + target && setFocusedOptionId(target.id) + }} + > + No results found. - {filteredOptions.map((option, index) => ( + {filteredOptions.map(option => ( handleSelect(option)} className={cn( - 'px-1.5 py-1', - focusedIndex === index && 'bg-secondary' + 'px-1.5 py-1 data-[selected=true]:bg-secondary data-[selected=true]:text-foreground' )} - ref={el => { - if (itemRefs.current) { - itemRefs.current[index] = el - } - }} > {option.customRenderItem ? ( diff --git a/src/webview/components/chat/sidebar/chat-sidebar.tsx b/src/webview/components/chat/sidebar/chat-sidebar.tsx index aad3df3..1f09e93 100644 --- a/src/webview/components/chat/sidebar/chat-sidebar.tsx +++ b/src/webview/components/chat/sidebar/chat-sidebar.tsx @@ -1,57 +1,49 @@ -// src/webview/components/chat/sidebar/chat-sidebar.tsx import React from 'react' -import { ArchiveIcon, PlusIcon } from '@radix-ui/react-icons' -import { ButtonWithTooltip } from '@webview/components/button-with-tooltip' +import { PlusIcon, TrashIcon } from '@radix-ui/react-icons' import { Button } from '@webview/components/ui/button' -import { logger } from '@webview/utils/logger' - -interface ChatHistoryItem { - id: string - title: string -} - -const useChatHistory = (): ChatHistoryItem[] => [ - { id: '1', title: 'Chat 1' }, - { id: '2', title: 'Chat 2' }, - { id: '3', title: 'Chat 3' } -] +import { useChatContext } from '@webview/contexts/chat-context' +import { useChatSessionsUI } from '@webview/hooks/chat/use-chat-sessions-ui' +import { cn } from '@webview/utils/common' export const ChatSidebar: React.FC = () => { - const chatHistory = useChatHistory() - - const handleNewChat = () => { - logger.log('New chat created') - } + const { context, createAndSwitchToNewSession, deleteSession, switchSession } = + useChatContext() - const handleArchiveChat = (id: string) => { - console.log(`Chat ${id} archived`) - } + const { chatSessionForRender } = useChatSessionsUI() return (
diff --git a/src/webview/components/global-search/global-search.tsx b/src/webview/components/global-search/global-search.tsx new file mode 100644 index 0000000..0de62f0 --- /dev/null +++ b/src/webview/components/global-search/global-search.tsx @@ -0,0 +1,242 @@ +import React, { useEffect, useRef, useState } from 'react' +import { + Command, + CommandDialog, + CommandEmpty, + CommandInput, + CommandItem, + CommandList +} from '@webview/components/ui/command' +import { + Popover, + PopoverContent, + PopoverTrigger +} from '@webview/components/ui/popover' +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger +} from '@webview/components/ui/tabs' +import { useControllableState } from '@webview/hooks/use-controllable-state' +import { useKeyboardNavigation } from '@webview/hooks/use-keyboard-navigation' +import { cn } from '@webview/utils/common' + +import { + KeyboardShortcutsInfo, + type ShortcutInfo +} from '../keyboard-shortcuts-info' + +interface SearchCategory { + id: string + name: string + items: SearchItem[] +} + +interface SearchItem { + id: string + label: string + icon?: React.ReactNode + keywords?: string[] + onSelect: () => void + renderPreview?: () => React.ReactNode + renderItem?: () => React.ReactNode +} + +interface GlobalSearchProps { + categories: SearchCategory[] + categoriesIsResult?: boolean + onOpenChange?: (open: boolean) => void + open?: boolean + activeCategory?: string + onActiveCategoryChange?: (category: string) => void + searchQuery?: string + onSearchQueryChange?: (query: string) => void +} + +const keyboardShortcuts: ShortcutInfo[] = [ + { key: ['↑', '↓'], description: 'Navigate', weight: 10 }, + { key: '↵', description: 'Select', weight: 9 }, + { key: '⇥', description: 'Switch tab', weight: 8 }, + { key: 'esc', description: 'Close', weight: 7 } +] + +export const GlobalSearch: React.FC = ({ + categories, + categoriesIsResult, + onOpenChange, + open: openProp, + activeCategory: activeCategoryProp, + onActiveCategoryChange, + searchQuery: searchQueryProp, + onSearchQueryChange +}) => { + const [isOpen, setIsOpen] = useControllableState({ + prop: openProp, + defaultProp: false, + onChange: onOpenChange + }) + + const [activeCategory, setActiveCategory] = useControllableState({ + prop: activeCategoryProp, + defaultProp: 'all', + onChange: onActiveCategoryChange + }) + + const [searchQuery, setSearchQuery] = useControllableState({ + prop: searchQueryProp, + defaultProp: '', + onChange: onSearchQueryChange + }) + + const [filteredItems, setFilteredItems] = useState([]) + + const [focusedItem, setFocusedItem] = useState(null) + + useEffect(() => { + const items = + activeCategory === 'all' + ? categories.flatMap(c => c.items) + : categories.find(c => c.id === activeCategory)?.items || [] + + if (categoriesIsResult) { + setFilteredItems(items) + } else { + setFilteredItems( + items.filter(item => { + const searchLower = searchQuery?.toLowerCase() || '' + return ( + item.label.toLowerCase().includes(searchLower) || + item.keywords?.some(keyword => + keyword.toLowerCase().includes(searchLower) + ) + ) + }) + ) + } + }, [activeCategory, searchQuery, categories, categoriesIsResult]) + + const handleOpenChange = (open: boolean) => { + setIsOpen(open) + if (!open) { + setSearchQuery('') + } + } + + const finalCategories = [ + { + id: 'all', + name: 'All', + items: categories.flatMap(c => c.items) + }, + ...categories + ] + + const tabRefs = useRef<(HTMLButtonElement | null)[]>([]) + + const { handleKeyDown, setFocusedIndex } = useKeyboardNavigation({ + itemCount: finalCategories.length, + itemRefs: tabRefs, + mode: 'tab', + defaultStartIndex: finalCategories.findIndex(t => t.id === activeCategory), + onTab: (_, index) => setActiveCategory(finalCategories[index]?.id ?? 'all') + }) + + return ( + +
+ { + const target = filteredItems.find(item => item.id === val) + target && setFocusedItem(target) + }} + onKeyDown={handleKeyDown} + > + + { + setActiveCategory(val) + setFocusedIndex(finalCategories.findIndex(t => t.id === val)) + }} + > + + {finalCategories.map(category => ( + + {category.name} + + ))} + + + {finalCategories.map(category => ( + + + + ))} + + + + + + +
+ + e.preventDefault()} + onCloseAutoFocus={e => e.preventDefault()} + onKeyDown={e => e.stopPropagation()} + > + {focusedItem?.renderPreview?.()} + + +
+ + ) +} + +const SearchResultList: React.FC<{ + filteredItems: SearchItem[] +}> = ({ filteredItems }) => ( + + {!filteredItems?.length ? ( + No results found. + ) : ( + filteredItems.map(item => ( + + {item.renderItem ? ( + item.renderItem() + ) : ( +
+ {item.icon && {item.icon}} + {item.label} +
+ )} +
+ )) + )} +
+) diff --git a/src/webview/components/sidebar-layout.tsx b/src/webview/components/sidebar-layout.tsx index 2fb73d1..82d5e39 100644 --- a/src/webview/components/sidebar-layout.tsx +++ b/src/webview/components/sidebar-layout.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/no-unstable-nested-components */ // src/webview/components/layout/sidebar-layout.tsx -import React from 'react' +import React, { useState } from 'react' import { TextAlignJustifyIcon } from '@radix-ui/react-icons' import { VisuallyHidden } from '@radix-ui/react-visually-hidden' import { Button, type ButtonProps } from '@webview/components/ui/button' @@ -16,6 +16,41 @@ import { cn } from '@webview/utils/common' import { SidebarHeader } from './sidebar-header' +const SidebarHamburger = ( + props: ButtonProps & { + open?: boolean + setOpen?: (open: boolean) => void + sidebar: React.ReactNode + } +) => { + const { open, setOpen, sidebar, ...otherProps } = props + + return ( + + + + + + + + + + + + + {sidebar} + + + ) +} + interface SidebarLayoutProps { title: string sidebar: React.ReactNode @@ -33,6 +68,8 @@ export const SidebarLayout: React.FC = ({ headerLeft, showBackButton }) => { + const [openSidebar, setOpenSidebar] = useState(false) + const Sidebar = () => (

{title}

@@ -40,26 +77,6 @@ export const SidebarLayout: React.FC = ({
) - const SidebarHamburger = (props: ButtonProps) => ( - - - - - - - - - - - - - - - - ) - return (
@@ -72,13 +89,17 @@ export const SidebarLayout: React.FC = ({ showBackButton={showBackButton} headerLeft={ <> - + } + /> {headerLeft} } />
-
{children}
diff --git a/src/webview/components/ui/command.tsx b/src/webview/components/ui/command.tsx index 45f70f5..281f77e 100644 --- a/src/webview/components/ui/command.tsx +++ b/src/webview/components/ui/command.tsx @@ -1,7 +1,14 @@ import * as React from 'react' import { type DialogProps } from '@radix-ui/react-dialog' import { MagnifyingGlassIcon } from '@radix-ui/react-icons' -import { Dialog, DialogContent } from '@webview/components/ui/dialog' +import { VisuallyHidden } from '@radix-ui/react-visually-hidden' +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle +} from '@webview/components/ui/dialog' import { cn } from '@webview/utils/common' import { Command as CommandPrimitive } from 'cmdk' @@ -23,7 +30,17 @@ interface CommandDialogProps extends DialogProps {} const CommandDialog = ({ children, ...props }: CommandDialogProps) => ( - + + + + + + + + {children} diff --git a/src/webview/components/ui/dialog.tsx b/src/webview/components/ui/dialog.tsx index 5c75289..270828e 100644 --- a/src/webview/components/ui/dialog.tsx +++ b/src/webview/components/ui/dialog.tsx @@ -26,8 +26,10 @@ const DialogOverlay: React.FC< DialogOverlay.displayName = DialogPrimitive.Overlay.displayName const DialogContent: React.FC< - React.ComponentPropsWithRef -> = ({ ref, className, children, ...props }) => ( + React.ComponentPropsWithRef & { + hideClose?: boolean + } +> = ({ ref, className, children, hideClose, ...props }) => ( {children} - - - Close - + {!hideClose && ( + + + Close + + )} ) diff --git a/src/webview/contexts/chat-context.tsx b/src/webview/contexts/chat-context.tsx new file mode 100644 index 0000000..9590d7e --- /dev/null +++ b/src/webview/contexts/chat-context.tsx @@ -0,0 +1,54 @@ +import React, { createContext, FC, useContext, useEffect } from 'react' +import type { ChatContext as IChatContext } from '@shared/types/chat-context' +import { useCallbackRef } from '@webview/hooks/use-callback-ref' +import { useChatStore, type ChatStore } from '@webview/stores/chat-store' +import { useChatUIStore, type ChatUIStore } from '@webview/stores/chat-ui-store' + +type ChatContextValue = ChatStore & + ChatUIStore & { + getContext: () => IChatContext + } + +const ChatContext = createContext(null) + +export const useChatContext = () => { + const context = useContext(ChatContext) + if (!context) { + throw new Error('useChatContext must be used within a ChatContextProvider') + } + return context +} + +export const ChatContextProvider: FC = ({ + children +}) => { + const chatStore = useChatStore() + const chatUIStore = useChatUIStore() + const { refreshChatSessions } = chatStore + + useEffect(() => { + refreshChatSessions() + }, [refreshChatSessions]) + + const getContext = useCallbackRef(() => chatStore.context) + + return ( + + {children} + + ) +} + +export const withChatContext =

( + WrappedComponent: React.ComponentType

+) => { + const WithChatContext: FC

= props => ( + + + + ) + + WithChatContext.displayName = `WithChatContext(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})` + + return WithChatContext +} diff --git a/src/webview/contexts/global-search-context.tsx b/src/webview/contexts/global-search-context.tsx new file mode 100644 index 0000000..61e4181 --- /dev/null +++ b/src/webview/contexts/global-search-context.tsx @@ -0,0 +1,176 @@ +import React, { createContext, useContext, useState } from 'react' +import type { ChatSession } from '@extension/webview-api/lowdb/chat-sessions-db' +import { useQuery } from '@tanstack/react-query' +import { GlobalSearch } from '@webview/components/global-search/global-search' +import type { SettingItem } from '@webview/components/settings/settings' +import { settingsConfig } from '@webview/components/settings/settings-config' +import { api } from '@webview/services/api-client' +import { useDebounce, useKey } from 'react-use' + +interface SearchResult { + type: 'chatSession' | 'setting' + item: ChatSession | SettingItem +} + +interface GlobalSearchContextType { + searchQuery: string + setSearchQuery: (query: string) => void + searchResults: SearchResult[] + isSearching: boolean + isOpen: boolean + openSearch: () => void + closeSearch: () => void + activeCategory: string + setActiveCategory: (category: string) => void +} + +const GlobalSearchContext = createContext( + undefined +) + +export const useGlobalSearch = () => { + const context = useContext(GlobalSearchContext) + if (!context) { + throw new Error( + 'useGlobalSearch must be used within a GlobalSearchProvider' + ) + } + return context +} + +export const GlobalSearchProvider: React.FC = ({ + children +}) => { + const [isOpen, setIsOpen] = useState(false) + const [searchQuery, setSearchQuery] = useState('') + const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('') + const [activeCategory, setActiveCategory] = useState('all') + + useDebounce( + () => { + setDebouncedSearchQuery(searchQuery) + }, + 300, + [searchQuery] + ) + + const openSearch = () => setIsOpen(true) + const closeSearch = () => { + setIsOpen(false) + setSearchQuery('') + setActiveCategory('') + } + + const { data: searchResults = [], isLoading: isSearching } = useQuery({ + queryKey: ['globalSearch', debouncedSearchQuery], + queryFn: async () => { + if (!debouncedSearchQuery) return [] + + const chatSessions = await api.chatSession.searchSessions({ + query: debouncedSearchQuery + }) + const settingsResults = searchSettings(debouncedSearchQuery) + + return [ + ...chatSessions.map(session => ({ + type: 'chatSession' as const, + item: session + })), + ...settingsResults.map(setting => ({ + type: 'setting' as const, + item: setting + })) + ] + }, + enabled: debouncedSearchQuery.length > 0 && isOpen + }) + + const contextValue = { + searchQuery, + setSearchQuery, + searchResults, + isSearching, + isOpen, + openSearch, + closeSearch, + activeCategory, + setActiveCategory + } + + return ( + + + result.type === 'chatSession') + .map(result => { + const chatSession = result.item as ChatSession + return { + id: chatSession.id, + label: chatSession.title, + keywords: [chatSession.title], + onSelect: () => { + /* handle selection */ + } + } + }) + }, + { + id: 'settings', + name: 'Settings', + items: searchResults + .filter(result => result.type === 'setting') + .map(result => ({ + id: (result.item as SettingItem).key, + label: (result.item as SettingItem).label, + onSelect: () => { + /* handle selection */ + } + })) + } + ]} + open={isOpen} + onOpenChange={setIsOpen} + activeCategory={activeCategory} + onActiveCategoryChange={setActiveCategory} + searchQuery={searchQuery} + onSearchQueryChange={setSearchQuery} + /> + {children} + + + ) +} + +const searchSettings = (query: string): SettingItem[] => { + const allSettings = settingsConfig.categories.flatMap( + category => category.settings || [] + ) + return allSettings.filter( + setting => + setting.label.toLowerCase().includes(query.toLowerCase()) || + setting.description.toLowerCase().includes(query.toLowerCase()) + ) +} + +const GlobalSearchListener: React.FC = ({ + children +}) => { + const { openSearch } = useGlobalSearch() + + useKey( + event => (event.metaKey || event.ctrlKey) && event.key === 'k', + event => { + event.preventDefault() + openSearch() + }, + { event: 'keydown' } + ) + + return <>{children} +} diff --git a/src/webview/contexts/plugin-registry-context.tsx b/src/webview/contexts/plugin-registry-context.tsx index 86203f5..3b77d66 100644 --- a/src/webview/contexts/plugin-registry-context.tsx +++ b/src/webview/contexts/plugin-registry-context.tsx @@ -7,14 +7,14 @@ import React, { } from 'react' import { ClientPluginRegistry } from '@shared/plugins/base/client/client-plugin-registry' import { createClientPlugins } from '@shared/plugins/base/client/create-client-plugins' -import type { PluginId } from '@shared/plugins/base/types' +import { PluginId } from '@shared/plugins/base/types' import { useQueryClient } from '@tanstack/react-query' import { useAsyncEffect } from '@webview/hooks/use-async-effect' import { useCallbackRef } from '@webview/hooks/use-callback-ref' -import { useImmer } from 'use-immer' +import { useImmer, type DraftFunction } from 'use-immer' const PluginRegistryContext = createContext<{ - pluginRegistry: ClientPluginRegistry | null + pluginRegistryRef: React.RefObject isPluginRegistryLoaded: boolean } | null>(null) @@ -30,11 +30,14 @@ export const PluginRegistryProvider: FC = ({ const getState = useCallbackRef( (pluginId: PluginId) => pluginStates[pluginId] || {} ) - const setState = useCallbackRef((pluginId: PluginId, newState: any) => { - updatePluginStates(draft => { - draft[pluginId] = newState - }) - }) + const setState = useCallbackRef( + (pluginId: PluginId, newState: any | DraftFunction) => { + updatePluginStates(draft => { + draft[pluginId] = newState + }) + } + ) + const pluginRegistryRef = useRef(null) useAsyncEffect( @@ -61,11 +64,18 @@ export const PluginRegistryProvider: FC = ({ [getState, setState] ) + // eslint-disable-next-line react-compiler/react-compiler + if (isLoaded && pluginRegistryRef.current) { + // eslint-disable-next-line react-compiler/react-compiler + pluginRegistryRef.current!.getState = (pluginId: PluginId) => + pluginStates[pluginId] || {} + } + return ( diff --git a/src/webview/contexts/providers.tsx b/src/webview/contexts/providers.tsx index 9c8e454..92199d0 100644 --- a/src/webview/contexts/providers.tsx +++ b/src/webview/contexts/providers.tsx @@ -6,6 +6,8 @@ import { Toaster } from '@webview/components/ui/sonner' import { createQueryClient } from '@webview/services/react-query/query-client' import { ThemeProvider as NextThemesProvider } from 'next-themes' +import { GlobalSearchProvider } from './global-search-context' + export function Providers({ children }: React.PropsWithChildren) { const queryClientRef = useRef(null) if (!queryClientRef.current) { @@ -22,7 +24,7 @@ export function Providers({ children }: React.PropsWithChildren) { - {children} + {children} diff --git a/src/webview/hooks/chat/use-chat-context-manager.ts b/src/webview/hooks/chat/use-chat-context-manager.ts deleted file mode 100644 index 4510f60..0000000 --- a/src/webview/hooks/chat/use-chat-context-manager.ts +++ /dev/null @@ -1,291 +0,0 @@ -import { - code, - content, - content2 -} from '@webview/components/chat/messages/markdown/test-data' -import { - ChatContextType, - type ChatContext, - type Conversation -} from '@webview/types/chat' -import { useImmer } from 'use-immer' -import { v4 as uuidv4 } from 'uuid' - -// eslint-disable-next-line unused-imports/no-unused-vars -const mockData = [ - { id: '1', createdAt: 1625234567890, role: 'human', contents: 'Hello!' }, - { - id: '2', - createdAt: 1625234567891, - role: 'ai', - contents: [ - { - type: 'text', - text: 'Hi there! How can I assist you today? Hi there! How can I assist you today? Hi there! How can I assist you today? Hi there! How can I assist you today? Hi there! How can I assist you today?' - } - ] - }, - { - id: '3', - createdAt: 1625234567892, - role: 'human', - contents: [ - { - type: 'text', - text: 'What’s the weather like?' - } - ] - }, - { - id: '4', - createdAt: 1625234567893, - role: 'ai', - contents: [ - { - type: 'text', - text: 'It’s sunny and warm today.' - } - ] - }, - { - id: '5', - createdAt: 1625234567894, - role: 'human', - contents: [ - { - type: 'text', - text: 'Thank you!' - } - ] - }, - { - id: '6', - createdAt: 1625234567895, - role: 'ai', - // contents: 'You’re welcome!' - contents: [ - { - type: 'text', - text: 'You’re welcome!' - } - ] - }, - { - id: '7', - createdAt: 1625234567896, - role: 'human', - contents: [ - { - type: 'text', - text: 'Can you tell me a joke?Can you tell me a joke?Can you tell me a joke?Can you tell me a joke?Can you tell me a joke?Can you tell me a joke?' - } - ] - }, - { - id: '8', - createdAt: 1625234567897, - role: 'ai', - contents: [ - { - type: 'text', - text: 'Why don’t scientists trust atoms? Because they make up everything!' - } - ] - }, - { - id: '9', - createdAt: 1625234567898, - role: 'human', - contents: [ - { - type: 'text', - text: 'Haha, that’s a good one.' - } - ] - }, - { - id: '10', - createdAt: 1625234567899, - role: 'ai', - contents: [ - { - type: 'text', - text: 'Glad you liked it!' - } - ] - }, - { - id: '11', - createdAt: 1625234567900, - role: 'human', - contents: [ - { - type: 'text', - text: 'What’s your favorite color?' - } - ] - }, - { - id: '12', - createdAt: 1625234567901, - role: 'ai', - contents: [ - { - type: 'text', - text: 'I like blue. It reminds me of the sky.' - } - ] - }, - { - id: '13', - createdAt: 1625234567902, - role: 'human', - contents: [ - { - type: 'text', - text: 'Do you have any hobbies?' - } - ] - }, - { - id: '14', - createdAt: 1625234567903, - role: 'ai', - contents: [ - { - type: 'text', - text: 'I enjoy learning new things and helping people!' - } - ] - }, - { - id: '15', - createdAt: 1625234567904, - role: 'human', - contents: [ - { - type: 'text', - text: 'That’s great to hear.' - } - ] - }, - { id: '16', createdAt: 1625234567905, role: 'ai', contents: code }, - { - id: '17', - createdAt: 1625234567906, - role: 'human', - contents: [ - { - type: 'text', - text: 'What’s the capital of France?' - } - ] - }, - { - id: '18', - createdAt: 1625234567907, - role: 'ai', - contents: [ - { - type: 'text', - text: content - } - ] - }, - { - id: '19', - createdAt: 1625234567908, - role: 'human', - contents: [ - { - type: 'text', - text: 'I appreciate your help.' - } - ] - }, - { - id: '20', - createdAt: 1625234567909, - role: 'ai', - contents: [ - { - type: 'text', - text: content2 - } - ] - }, - { - id: '21', - createdAt: 1625234567910, - role: 'human', - contents: [ - { - type: 'text', - text: 'What’s 2 + 2?' - } - ] - }, - { - id: '22', - createdAt: 1625234567911, - role: 'ai', - contents: [ - { - type: 'text', - text: '2 + 2 equals 4.' - } - ] - } -] as Conversation[] - -export const useChatContextManager = () => { - const [context, setContext] = useImmer(() => ({ - id: uuidv4(), - type: ChatContextType.Chat, - createdAt: Date.now(), - updatedAt: Date.now(), - settings: { - allowLongFileScan: false, - explicitContext: '总是用中文回复', - fastApplyModelName: 'gpt-4o-mini', - modelName: 'gpt-4o', - useFastApply: true - }, - conversations: [] // [...mockData] - })) - - const getConversation = (id: string) => - context.conversations.find(conversation => conversation.id === id) - - const addConversation = (conversation: Conversation) => { - setContext(draft => { - draft.conversations.push(conversation) - }) - } - - const updateConversation = (id: string, conversation: Conversation) => { - setContext(draft => { - const index = draft.conversations.findIndex( - conversation => conversation.id === id - ) - draft.conversations[index] = conversation - }) - } - - const deleteConversation = (id: string) => { - setContext(draft => { - draft.conversations = draft.conversations.filter( - conversation => conversation.id !== id - ) - }) - } - - return { - context, - setContext, - getConversation, - addConversation, - updateConversation, - deleteConversation - } -} diff --git a/src/webview/hooks/chat/use-chat-sessions-ui.ts b/src/webview/hooks/chat/use-chat-sessions-ui.ts new file mode 100644 index 0000000..691c047 --- /dev/null +++ b/src/webview/hooks/chat/use-chat-sessions-ui.ts @@ -0,0 +1,29 @@ +import { useEffect } from 'react' +import { convertChatContextToChatSession } from '@shared/utils/convert-chat-context-to-chat-session' +import { useChatContext } from '@webview/contexts/chat-context' + +export const useChatSessionsUI = () => { + const { context, chatSessions, switchSession } = useChatContext() + + const isCurrentSessionInChatSessions = + chatSessions?.some(session => session.id === context.id) ?? false + + const currentSession = convertChatContextToChatSession(context) + + const chatSessionForRender = ( + isCurrentSessionInChatSessions ? [...chatSessions]! : [currentSession] + ).sort((a, b) => b.updatedAt - a.updatedAt) + + useEffect(() => { + if (chatSessions?.length && !isCurrentSessionInChatSessions) { + const lastSession = [...chatSessions].sort( + (a, b) => b.updatedAt - a.updatedAt + )[0]! + switchSession(lastSession.id) + } + }, [chatSessions, isCurrentSessionInChatSessions, switchSession]) + + return { + chatSessionForRender + } +} diff --git a/src/webview/hooks/chat/use-chat-state.ts b/src/webview/hooks/chat/use-chat-state.ts index d3b468b..23b3c00 100644 --- a/src/webview/hooks/chat/use-chat-state.ts +++ b/src/webview/hooks/chat/use-chat-state.ts @@ -1,11 +1,21 @@ -import type { ChatContext, Conversation } from '@webview/types/chat' +import { useMemo } from 'react' +import type { Conversation } from '@shared/types/chat-context' +import { useChatContext } from '@webview/contexts/chat-context' +import type { ConversationUIState } from '@webview/types/chat' +import type { DraftFunction } from 'use-immer' -import { useChatContextManager } from './use-chat-context-manager' import { useConversation } from './use-conversation' -import { useConversationsWithUIState } from './use-conversations-with-ui-state' export const useChatState = () => { - const { context, setContext } = useChatContextManager() + const { + context, + setContext, + addConversation, + getConversationUIState, + setConversationUIState, + batchSetConversationUIState + } = useChatContext() + const { conversation: newConversation, setConversation: setNewConversation, @@ -14,64 +24,76 @@ export const useChatState = () => { const allConversations = [...context.conversations, newConversation] - const { - conversationsWithUIState, - getConversationUIState, - setConversationUIState, - setAllConversationsUIState - } = useConversationsWithUIState({ - conversations: allConversations - }) + const conversationsWithUIState = allConversations.map(conversation => ({ + ...conversation, + uiState: getConversationUIState(context.id, conversation.id) + })) const historiesConversationsWithUIState = conversationsWithUIState.filter( c => c.id !== newConversation.id ) - const newConversationUIState = getConversationUIState(newConversation.id) - - const replaceConversationAndTruncate = ( - conversation: Conversation - ): Promise => - new Promise(resolve => { - setContext(draft => { - const index = draft.conversations.findIndex( - c => c.id === conversation.id - ) + const newConversationUIState = useMemo( + () => getConversationUIState(context.id, newConversation.id), + [context, newConversation.id] + ) - // delete current conversation and all conversations after it - index !== -1 && draft.conversations.splice(index) + const replaceConversationAndTrimHistory = (conversation: Conversation) => { + setContext(draft => { + const index = draft.conversations.findIndex(c => c.id === conversation.id) - // add new conversation - draft.conversations.push(conversation) + // delete current conversation and all conversations after it + index !== -1 && draft.conversations.splice(index) - // Create a copy of the updated context to resolve the promise - const updatedContext = JSON.parse(JSON.stringify(draft)) - resolve(updatedContext) - }) + // add new conversation + draft.conversations.push(conversation) }) + } - const setUIStateForSending = (conversationId: string) => { - setAllConversationsUIState({ sendButtonDisabled: true }) - setConversationUIState(conversationId, { isLoading: true }) + const setAllConversationsUIState = ( + uiStateOrUpdater: ConversationUIState | DraftFunction + ) => { + batchSetConversationUIState( + context.id, + allConversations.map(c => c.id), + uiStateOrUpdater + ) } - const resetUIStateAfterSending = (conversationId: string) => { - setAllConversationsUIState({ sendButtonDisabled: false }) - setConversationUIState(conversationId, { - isLoading: false, - isEditMode: false + const prepareUIForSending = (conversationId: string) => { + setAllConversationsUIState(draft => { + draft.sendButtonDisabled = true + }) + setConversationUIState(context.id, conversationId, draft => { + draft.isLoading = true }) } - const setConversationEditMode = ( + const resetUIAfterSending = (conversationId: string) => { + setAllConversationsUIState(draft => { + draft.sendButtonDisabled = false + }) + setConversationUIState(context.id, conversationId, draft => { + draft.isLoading = false + draft.isEditMode = false + }) + } + + const toggleConversationEditMode = ( conversationId: string, isEditMode: boolean ) => { if (!isEditMode) { - setConversationUIState(conversationId, { isEditMode: false }) + setConversationUIState(context.id, conversationId, draft => { + draft.isEditMode = false + }) } else { - setAllConversationsUIState({ isEditMode: false }) - setConversationUIState(conversationId, { isEditMode: true }) + setAllConversationsUIState(draft => { + draft.isEditMode = false + }) + setConversationUIState(context.id, conversationId, draft => { + draft.isEditMode = true + }) } } @@ -84,9 +106,10 @@ export const useChatState = () => { conversationsWithUIState, historiesConversationsWithUIState, newConversationUIState, - replaceConversationAndTruncate, - setUIStateForSending, - resetUIStateAfterSending, - setConversationEditMode + replaceConversationAndTrimHistory, + prepareUIForSending, + resetUIAfterSending, + toggleConversationEditMode, + addConversation } } diff --git a/src/webview/hooks/chat/use-conversations-with-ui-state.ts b/src/webview/hooks/chat/use-conversations-with-ui-state.ts deleted file mode 100644 index 249aab8..0000000 --- a/src/webview/hooks/chat/use-conversations-with-ui-state.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { Conversation, ConversationUIState } from '@webview/types/chat' -import { useImmer } from 'use-immer' - -export interface UseConversationsWithUIStateOptions { - conversations: Conversation[] -} - -export const useConversationsWithUIState = ( - options: UseConversationsWithUIStateOptions -) => { - const { conversations } = options - const [conversationIdUIStateMap, setConversationIdUIStateMap] = useImmer< - Record - >({}) - - const conversationsWithUIState = conversations.map(conversation => ({ - ...conversation, - uiState: - conversationIdUIStateMap[conversation.id] || - ({} satisfies ConversationUIState) - })) - - const getConversationUIState = (conversationId: string) => - conversationIdUIStateMap[conversationId] || - ({} satisfies ConversationUIState) - - const setConversationUIState = ( - conversationId: string, - uiState: Partial - ) => { - setConversationIdUIStateMap(draft => { - draft[conversationId] = { - ...draft[conversationId], - ...uiState - } - }) - } - - const setAllConversationsUIState = ( - uiState: Partial - ) => { - setConversationIdUIStateMap(draft => { - conversations.forEach(conversation => { - draft[conversation.id] = { - ...draft[conversation.id], - ...uiState - } - }) - }) - } - - return { - conversationsWithUIState, - conversationIdUIStateMap, - setConversationIdUIStateMap, - getConversationUIState, - setConversationUIState, - setAllConversationsUIState - } -} diff --git a/src/webview/hooks/chat/use-mention-options.tsx b/src/webview/hooks/chat/use-mention-options.tsx index 50e7936..00dd21d 100644 --- a/src/webview/hooks/chat/use-mention-options.tsx +++ b/src/webview/hooks/chat/use-mention-options.tsx @@ -2,12 +2,13 @@ import { useQuery } from '@tanstack/react-query' import { usePluginRegistry } from '@webview/contexts/plugin-registry-context' export const useMentionOptions = () => { - const { pluginRegistry, isPluginRegistryLoaded } = usePluginRegistry() + const { pluginRegistryRef, isPluginRegistryLoaded } = usePluginRegistry() const { data: mentionOptions = [] } = useQuery({ queryKey: ['realtime', 'useMentionOptions', isPluginRegistryLoaded], queryFn: async () => { - const editorProvider = pluginRegistry?.providerManagers.editor.mergeAll() + const editorProvider = + pluginRegistryRef.current?.providerManagers.editor.mergeAll() const result = (await editorProvider?.getMentionOptions?.()) || [] return result diff --git a/src/webview/hooks/chat/use-plugin-providers.ts b/src/webview/hooks/chat/use-plugin-providers.ts index e26078f..7e8c00d 100644 --- a/src/webview/hooks/chat/use-plugin-providers.ts +++ b/src/webview/hooks/chat/use-plugin-providers.ts @@ -2,9 +2,9 @@ import { useMemo } from 'react' import { usePluginRegistry } from '@webview/contexts/plugin-registry-context' export const usePluginEditorProviders = () => { - const { pluginRegistry, isPluginRegistryLoaded } = usePluginRegistry() + const { pluginRegistryRef, isPluginRegistryLoaded } = usePluginRegistry() const merged = useMemo( - () => pluginRegistry?.providerManagers.editor.mergeAll() || {}, + () => pluginRegistryRef.current?.providerManagers.editor.mergeAll() || {}, [isPluginRegistryLoaded] ) @@ -12,19 +12,25 @@ export const usePluginEditorProviders = () => { } export const usePluginFilesSelectorProviders = () => { - const { pluginRegistry, isPluginRegistryLoaded } = usePluginRegistry() + const { pluginRegistryRef, isPluginRegistryLoaded } = usePluginRegistry() const merged = useMemo( - () => pluginRegistry?.providerManagers.filesSelector.mergeAll() || {}, + () => + pluginRegistryRef.current?.providerManagers.filesSelector.mergeAll() || + {}, [isPluginRegistryLoaded] ) - return merged + const selectedFiles = merged.getSelectedFiles?.() || [] + + return { ...merged, selectedFiles } } export const usePluginImagesSelectorProviders = () => { - const { pluginRegistry, isPluginRegistryLoaded } = usePluginRegistry() + const { pluginRegistryRef, isPluginRegistryLoaded } = usePluginRegistry() const merged = useMemo( - () => pluginRegistry?.providerManagers.imagesSelector.mergeAll() || {}, + () => + pluginRegistryRef.current?.providerManagers.imagesSelector.mergeAll() || + {}, [isPluginRegistryLoaded] ) @@ -32,9 +38,9 @@ export const usePluginImagesSelectorProviders = () => { } export const usePluginStates = () => { - const { pluginRegistry, isPluginRegistryLoaded } = usePluginRegistry() + const { pluginRegistryRef, isPluginRegistryLoaded } = usePluginRegistry() const states = useMemo( - () => pluginRegistry?.providerManagers.state.getAll() || {}, + () => pluginRegistryRef.current?.providerManagers.state.getAll() || {}, [isPluginRegistryLoaded] ) diff --git a/src/webview/hooks/use-keyboard-navigation.ts b/src/webview/hooks/use-keyboard-navigation.ts index 1920087..6889aac 100644 --- a/src/webview/hooks/use-keyboard-navigation.ts +++ b/src/webview/hooks/use-keyboard-navigation.ts @@ -1,5 +1,7 @@ import React, { RefObject, useState } from 'react' +import { useCallbackRef } from './use-callback-ref' + interface UseKeyboardNavigationProps { itemCount: number itemRefs: RefObject<(HTMLElement | null)[]> @@ -129,18 +131,20 @@ export const useKeyboardNavigation = (props: UseKeyboardNavigationProps) => { } } + const getFocusedIndex = useCallbackRef(() => focusedIndex) + // for list item hover - const handleMouseOver = (event: React.MouseEvent) => { + const handleMouseMove = (event: React.MouseEvent) => { event.stopPropagation() event.preventDefault() - const target = event.relatedTarget as HTMLElement + const target = (event.relatedTarget || event.target) as HTMLElement const index = itemRefs.current?.findIndex( el => el === target || el?.contains(target) ) ?? -1 - if (index === -1 || index === focusedIndex) return + if (index === -1 || index === getFocusedIndex()) return setFocusedIndex(index) } @@ -150,7 +154,7 @@ export const useKeyboardNavigation = (props: UseKeyboardNavigationProps) => { setFocusedIndex, handleKeyDown, listEventHandlers: { - onMouseOver: handleMouseOver + onMouseMove: handleMouseMove } } } diff --git a/src/webview/main.tsx b/src/webview/main.tsx index 8d0001c..e1ea72b 100644 --- a/src/webview/main.tsx +++ b/src/webview/main.tsx @@ -1,6 +1,6 @@ import React from 'react' import ReactDOM from 'react-dom/client' -import { BrowserRouter } from 'react-router-dom' +import { HashRouter } from 'react-router-dom' import { getSocketPort } from './services/api-client/get-socket-port' @@ -17,9 +17,9 @@ async function main() { ReactDOM.createRoot(document.getElementById('app')!).render( - + - + ) } diff --git a/src/webview/stores/chat-store.ts b/src/webview/stores/chat-store.ts new file mode 100644 index 0000000..46f7d6a --- /dev/null +++ b/src/webview/stores/chat-store.ts @@ -0,0 +1,152 @@ +import { api } from '@webview/services/api-client' +import { + ChatContext, + ChatContextType, + ChatSession, + Conversation +} from '@webview/types/chat' +import { getErrorMsg } from '@webview/utils/common' +import { logger } from '@webview/utils/logger' +import { produce } from 'immer' +import { toast } from 'sonner' +import type { DraftFunction } from 'use-immer' +import { v4 as uuidv4 } from 'uuid' +import { create } from 'zustand' +import { immer } from 'zustand/middleware/immer' + +const createDefaultContext = (): ChatContext => ({ + id: uuidv4(), + type: ChatContextType.Chat, + createdAt: Date.now(), + updatedAt: Date.now(), + settings: { + allowLongFileScan: false, + explicitContext: '总是用中文回复', + fastApplyModelName: 'gpt-4o-mini', + modelName: 'gpt-4o', + useFastApply: true + }, + conversations: [] +}) + +export type ChatStore = { + context: ChatContext + chatSessions: ChatSession[] + setContext: ( + contextOrUpdater: ChatContext | DraftFunction + ) => void + getConversation: (id: string) => Conversation | undefined + addConversation: (conversation: Conversation) => void + updateConversation: (id: string, conversation: Conversation) => void + deleteConversation: (id: string) => void + resetContext: () => void + saveSession: () => Promise + refreshChatSessions: () => Promise + createAndSwitchToNewSession: () => Promise + deleteSession: (id: string) => Promise + switchSession: (sessionId: string) => Promise +} + +export const useChatStore = create()( + immer((set, get) => ({ + context: createDefaultContext(), + chatSessions: [] as ChatSession[], + setContext: contextOrUpdater => { + if (typeof contextOrUpdater === 'function') { + const newContext = produce(get().context, contextOrUpdater) + + set(state => { + state.context = newContext + }) + } else { + set(state => { + state.context = contextOrUpdater + }) + } + }, + getConversation: id => get().context.conversations.find(c => c.id === id), + addConversation: conversation => + set(state => { + state.context.conversations.push(conversation) + }), + updateConversation: (id, conversation) => + set(state => { + const index = state.context.conversations.findIndex(c => c.id === id) + if (index !== -1) { + state.context.conversations[index] = conversation + } + }), + deleteConversation: id => + set(state => { + state.context.conversations = state.context.conversations.filter( + c => c.id !== id + ) + }), + resetContext: () => set({ context: createDefaultContext() }), + saveSession: async () => { + try { + await api.chatSession.createOrUpdateSession({ + chatContext: get().context + }) + await get().refreshChatSessions() + } catch (error) { + logger.error('Failed to save session', error) + toast.error(`Failed to save session: ${getErrorMsg(error)}`) + } + }, + refreshChatSessions: async () => { + try { + const sessions = await api.chatSession.getAllSessions({}) + set(state => { + state.chatSessions = sessions.sort( + (a, b) => b.updatedAt - a.updatedAt + ) + }) + } catch (error) { + logger.error('Failed to refresh chat sessions', error) + toast.error(`Failed to refresh chat sessions: ${getErrorMsg(error)}`) + } + }, + createAndSwitchToNewSession: async () => { + try { + const newContext = createDefaultContext() + const newSession = await api.chatSession.createSession({ + chatContext: newContext + }) + await get().refreshChatSessions() + await get().switchSession(newSession.id) + logger.log('New chat created') + } catch (error) { + logger.error('Failed to create and switch to new chat', error) + } + }, + deleteSession: async id => { + try { + await api.chatSession.deleteSession({ sessionId: id }) + await get().refreshChatSessions() + if (get().context.id === id && get().chatSessions.length) { + get().switchSession(get().chatSessions[0]!.id) + } + + logger.log(`Chat ${id} deleted`) + } catch (error) { + logger.error(`Failed to delete chat ${id}`, error) + } + }, + switchSession: async sessionId => { + try { + if (get().context.id === sessionId) return + const fullChatContext = await api.chatSession.getChatContext({ + sessionId + }) + if (!fullChatContext) throw new Error('Chat context not found') + set({ context: fullChatContext }) + } catch (error) { + logger.error(`Failed to switch to session ${sessionId}`, error) + toast.error( + `Failed to switch to session ${sessionId}: ${getErrorMsg(error)}` + ) + } + } + })) +) diff --git a/src/webview/stores/chat-ui-store.ts b/src/webview/stores/chat-ui-store.ts new file mode 100644 index 0000000..4fbc3d5 --- /dev/null +++ b/src/webview/stores/chat-ui-store.ts @@ -0,0 +1,99 @@ +import { ConversationUIState } from '@webview/types/chat' +import { produce } from 'immer' +import type { DraftFunction } from 'use-immer' +import { create } from 'zustand' +import { immer } from 'zustand/middleware/immer' + +export type ChatUIStore = { + sessionConversationUIStateMap: Record + getConversationUIState: ( + sessionId: string, + conversationId: string + ) => ConversationUIState + setConversationUIState: ( + sessionId: string, + conversationId: string, + uiStateOrUpdater: ConversationUIState | DraftFunction + ) => void + batchSetConversationUIState: ( + sessionId: string, + conversationIds: string[], + uiStateOrUpdater: ConversationUIState | DraftFunction + ) => void +} + +export const useChatUIStore = create()( + immer((set, get) => ({ + sessionConversationUIStateMap: {}, + getConversationUIState: (sessionId: string, conversationId: string) => + get().sessionConversationUIStateMap[`${sessionId}_${conversationId}`] || + ({} as ConversationUIState), + setConversationUIState: ( + sessionId: string, + conversationId: string, + uiStateOrUpdater: ConversationUIState | DraftFunction + ) => { + const id = `${sessionId}_${conversationId}` + + if (typeof uiStateOrUpdater === 'function') { + const newUIState = produce( + get().sessionConversationUIStateMap[id] || {}, + uiStateOrUpdater + ) + set(state => { + if (!state.sessionConversationUIStateMap[id]) { + state.sessionConversationUIStateMap[id] = {} + } + + state.sessionConversationUIStateMap[id] = newUIState + }) + } else { + set(state => { + if (!state.sessionConversationUIStateMap[id]) { + state.sessionConversationUIStateMap[id] = {} + } + + state.sessionConversationUIStateMap[id] = uiStateOrUpdater + }) + } + }, + batchSetConversationUIState: ( + sessionId: string, + conversationIds: string[], + uiStateOrUpdater: ConversationUIState | DraftFunction + ) => { + if (typeof uiStateOrUpdater === 'function') { + const idUIStateMap = conversationIds.map(conversationId => { + const id = `${sessionId}_${conversationId}` + return { + id, + uiState: produce( + get().sessionConversationUIStateMap[id] || {}, + uiStateOrUpdater + ) + } + }) + + set(state => { + idUIStateMap.forEach(({ id, uiState }) => { + if (!state.sessionConversationUIStateMap[id]) { + state.sessionConversationUIStateMap[id] = {} + } + + state.sessionConversationUIStateMap[id] = uiState + }) + }) + } else { + set(state => { + conversationIds.forEach(conversationId => { + const id = `${sessionId}_${conversationId}` + if (!state.sessionConversationUIStateMap[id]) { + state.sessionConversationUIStateMap[id] = {} + } + state.sessionConversationUIStateMap[id] = uiStateOrUpdater + }) + }) + } + } + })) +) diff --git a/src/webview/types/chat.ts b/src/webview/types/chat.ts index d685c0e..db7424e 100644 --- a/src/webview/types/chat.ts +++ b/src/webview/types/chat.ts @@ -2,6 +2,7 @@ import type { FC } from 'react' import type { Conversation } from '@shared/types/chat-context' import type { MentionItemLayoutProps } from '@webview/components/chat/selectors/mention-selector/mention-item-layout' +export type { ChatSession } from '@extension/webview-api/lowdb/chat-sessions-db' export type { DocSite } from '@extension/webview-api/lowdb/doc-sites-db' export type { ProgressInfo } from '@extension/webview-api/chat-context-processor/utils/process-reporter' export * from '@shared/types/chat-context' diff --git a/vite.config.mts b/vite.config.mts index e4aa35e..10b4028 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -29,7 +29,8 @@ const define: Record = { } // https://vitejs.dev/config/ -export default defineConfig(() => { +export default defineConfig(env => { + const isBuild = env.command === 'build' process.env.APP_BUILD_TIME = `${Date.now()}` process.env.APP_VERSION = pkg.version @@ -46,7 +47,8 @@ export default defineConfig(() => { svgr(), pages({ dirs: 'src/webview/pages', - routeStyle: 'next' + routeStyle: 'next', + importMode: 'sync' }), vscode({ extension: { @@ -76,11 +78,22 @@ export default defineConfig(() => { } } ] + }, + webview: { + csp: isBuild ? '' : undefined } }) ], resolve: { dedupe: ['react', 'react-dom'] + }, + experimental: { + renderBuiltUrl(filename, { hostType }) { + if (hostType === 'js' && isBuild) { + return { runtime: `window.__assetsPath(${JSON.stringify(filename)})` } + } + return { relative: true } + } } } })