mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
Merge branch 'main' into main
This commit is contained in:
commit
b3fd408cf7
20 changed files with 939 additions and 598 deletions
|
|
@ -149,6 +149,7 @@ We keep track of all the files we've changed with Void so it's easy to rebase:
|
||||||
- CONTRIBUTING.md
|
- CONTRIBUTING.md
|
||||||
- VOID_USEFUL_LINKS.md
|
- VOID_USEFUL_LINKS.md
|
||||||
- product.json
|
- product.json
|
||||||
|
- package.json
|
||||||
|
|
||||||
- src/vs/workbench/api/common/{extHost.api.impl.ts | extHostApiCommands.ts}
|
- src/vs/workbench/api/common/{extHost.api.impl.ts | extHostApiCommands.ts}
|
||||||
- src/vs/workbench/workbench.common.main.ts
|
- src/vs/workbench/workbench.common.main.ts
|
||||||
|
|
|
||||||
2
extensions/void/.vscode/settings.json
vendored
2
extensions/void/.vscode/settings.json
vendored
|
|
@ -8,7 +8,7 @@
|
||||||
"**/.DS_Store": true,
|
"**/.DS_Store": true,
|
||||||
"**/Thumbs.db": true,
|
"**/Thumbs.db": true,
|
||||||
"out": false,
|
"out": false,
|
||||||
"**/node_modules": false
|
"**/node_modules": true
|
||||||
},
|
},
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"out": true // set this to false to include "out" folder in search results
|
"out": true // set this to false to include "out" folder in search results
|
||||||
|
|
|
||||||
|
|
@ -1 +1,11 @@
|
||||||
Please see the `CONTRIBUTING.md` for information on how to contribute :)!
|
Please see the `CONTRIBUTING.md` for information on how to contribute :)!
|
||||||
|
|
||||||
|
|
||||||
|
Here's an overview on how the extension works:
|
||||||
|
|
||||||
|
- The extension mounts in `extension.ts`.
|
||||||
|
|
||||||
|
- The Sidebar's HTML (everything in `sidebar/`) is built in React, and it's rendered by mounting a `<script>` tag - see `SidebarWebviewProvider.ts`.
|
||||||
|
|
||||||
|
- Communication between the sidebar script and the extension takes place via API. You can search for "postMessage" to see where API calls happen.
|
||||||
|
|
||||||
|
|
|
||||||
251
extensions/void/package-lock.json
generated
251
extensions/void/package-lock.json
generated
|
|
@ -75,9 +75,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@anthropic-ai/sdk/node_modules/@types/node": {
|
"node_modules/@anthropic-ai/sdk/node_modules/@types/node": {
|
||||||
"version": "18.19.54",
|
"version": "18.19.50",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.50.tgz",
|
||||||
"integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==",
|
"integrity": "sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~5.26.4"
|
||||||
|
|
@ -90,13 +90,13 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.25.7",
|
"version": "7.24.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
|
||||||
"integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==",
|
"integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/highlight": "^7.25.7",
|
"@babel/highlight": "^7.24.7",
|
||||||
"picocolors": "^1.0.0"
|
"picocolors": "^1.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -104,9 +104,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-validator-identifier": {
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
"version": "7.25.7",
|
"version": "7.24.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
|
||||||
"integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==",
|
"integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -114,13 +114,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/highlight": {
|
"node_modules/@babel/highlight": {
|
||||||
"version": "7.25.7",
|
"version": "7.24.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
|
||||||
"integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==",
|
"integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-validator-identifier": "^7.25.7",
|
"@babel/helper-validator-identifier": "^7.24.7",
|
||||||
"chalk": "^2.4.2",
|
"chalk": "^2.4.2",
|
||||||
"js-tokens": "^4.0.0",
|
"js-tokens": "^4.0.0",
|
||||||
"picocolors": "^1.0.0"
|
"picocolors": "^1.0.0"
|
||||||
|
|
@ -204,18 +204,18 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.23.1",
|
"version": "0.23.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
|
||||||
"integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
|
"integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"win32"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
|
@ -312,9 +312,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.12.0",
|
"version": "9.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.11.1.tgz",
|
||||||
"integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==",
|
"integrity": "sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -700,9 +700,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.7.4",
|
"version": "22.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.6.1.tgz",
|
||||||
"integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==",
|
"integrity": "sha512-V48tCfcKb/e6cVUigLAaJDAILdMP0fUW6BidkPK4GpGjXcfbnoHasCZDwz3N3yVt5we2RHm4XTQCpv0KJz9zqw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.19.2"
|
"undici-types": "~6.19.2"
|
||||||
|
|
@ -726,9 +726,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "18.3.11",
|
"version": "18.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.8.tgz",
|
||||||
"integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==",
|
"integrity": "sha512-syBUrW3/XpnW4WJ41Pft+I+aPoDVbrBVQGEnbD7NijDGlVC+8gV/XKRY+7vMDlfPpbwYt0l1vd/Sj8bJGMbs9Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -785,17 +785,17 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.8.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.7.0.tgz",
|
||||||
"integrity": "sha512-wORFWjU30B2WJ/aXBfOm1LX9v9nyt9D3jsSOxC3cCaTQGCW5k4jNpmjFv3U7p/7s4yvdjHzwtv2Sd2dOyhjS0A==",
|
"integrity": "sha512-RIHOoznhA3CCfSTFiB6kBGLQtB/sox+pJ6jeFu6FxJvqL8qRxq/FfGO/UhsGgQM9oGdXkV4xUgli+dt26biB6A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.8.0",
|
"@typescript-eslint/scope-manager": "8.7.0",
|
||||||
"@typescript-eslint/type-utils": "8.8.0",
|
"@typescript-eslint/type-utils": "8.7.0",
|
||||||
"@typescript-eslint/utils": "8.8.0",
|
"@typescript-eslint/utils": "8.7.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.8.0",
|
"@typescript-eslint/visitor-keys": "8.7.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^5.3.1",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
|
|
@ -819,16 +819,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.8.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.7.0.tgz",
|
||||||
"integrity": "sha512-uEFUsgR+tl8GmzmLjRqz+VrDv4eoaMqMXW7ruXfgThaAShO9JTciKpEsB+TvnfFfbg5IpujgMXVV36gOJRLtZg==",
|
"integrity": "sha512-lN0btVpj2unxHlNYLI//BQ7nzbMJYBVQX5+pbNXvGYazdlgYonMn4AhhHifQ+J4fGRYA/m1DjaQjx+fDetqBOQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.8.0",
|
"@typescript-eslint/scope-manager": "8.7.0",
|
||||||
"@typescript-eslint/types": "8.8.0",
|
"@typescript-eslint/types": "8.7.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.8.0",
|
"@typescript-eslint/typescript-estree": "8.7.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.8.0",
|
"@typescript-eslint/visitor-keys": "8.7.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -848,14 +848,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.8.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.7.0.tgz",
|
||||||
"integrity": "sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg==",
|
"integrity": "sha512-87rC0k3ZlDOuz82zzXRtQ7Akv3GKhHs0ti4YcbAJtaomllXoSO8hi7Ix3ccEvCd824dy9aIX+j3d2UMAfCtVpg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.8.0",
|
"@typescript-eslint/types": "8.7.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.8.0"
|
"@typescript-eslint/visitor-keys": "8.7.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
|
@ -866,14 +866,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.8.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.7.0.tgz",
|
||||||
"integrity": "sha512-IKwJSS7bCqyCeG4NVGxnOP6lLT9Okc3Zj8hLO96bpMkJab+10HIfJbMouLrlpyOr3yrQ1cA413YPFiGd1mW9/Q==",
|
"integrity": "sha512-tl0N0Mj3hMSkEYhLkjREp54OSb/FI6qyCzfiiclvJvOqre6hsZTGSnHtmFLDU8TIM62G7ygEa1bI08lcuRwEnQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.8.0",
|
"@typescript-eslint/typescript-estree": "8.7.0",
|
||||||
"@typescript-eslint/utils": "8.8.0",
|
"@typescript-eslint/utils": "8.7.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^1.3.0"
|
"ts-api-utils": "^1.3.0"
|
||||||
},
|
},
|
||||||
|
|
@ -891,9 +891,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.8.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.7.0.tgz",
|
||||||
"integrity": "sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw==",
|
"integrity": "sha512-LLt4BLHFwSfASHSF2K29SZ+ZCsbQOM+LuarPjRUuHm+Qd09hSe3GCeaQbcCr+Mik+0QFRmep/FyZBO6fJ64U3w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -905,14 +905,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.8.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.7.0.tgz",
|
||||||
"integrity": "sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw==",
|
"integrity": "sha512-MC8nmcGHsmfAKxwnluTQpNqceniT8SteVwd2voYlmiSWGOtjvGXdPl17dYu2797GVscK30Z04WRM28CrKS9WOg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.8.0",
|
"@typescript-eslint/types": "8.7.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.8.0",
|
"@typescript-eslint/visitor-keys": "8.7.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
|
|
@ -934,16 +934,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.8.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.7.0.tgz",
|
||||||
"integrity": "sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==",
|
"integrity": "sha512-ZbdUdwsl2X/s3CiyAu3gOlfQzpbuG3nTWKPoIvAu1pu5r8viiJvv2NPN2AqArL35NCYtw/lrPPfM4gxrMLNLPw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"@typescript-eslint/scope-manager": "8.8.0",
|
"@typescript-eslint/scope-manager": "8.7.0",
|
||||||
"@typescript-eslint/types": "8.8.0",
|
"@typescript-eslint/types": "8.7.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.8.0"
|
"@typescript-eslint/typescript-estree": "8.7.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
|
@ -957,13 +957,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.8.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.7.0.tgz",
|
||||||
"integrity": "sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==",
|
"integrity": "sha512-b1tx0orFCCh/THWPQa2ZwWzvOeyzzp36vkJYOpVg0u8UVOIsfVrnuC9FqAw9gRKn+rG2VmWQ/zDJZzkxUnj/XQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.8.0",
|
"@typescript-eslint/types": "8.7.0",
|
||||||
"eslint-visitor-keys": "^3.4.3"
|
"eslint-visitor-keys": "^3.4.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -1477,9 +1477,9 @@
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.24.0",
|
"version": "4.23.3",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
|
||||||
"integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==",
|
"integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -1497,8 +1497,8 @@
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001663",
|
"caniuse-lite": "^1.0.30001646",
|
||||||
"electron-to-chromium": "^1.5.28",
|
"electron-to-chromium": "^1.5.4",
|
||||||
"node-releases": "^2.0.18",
|
"node-releases": "^2.0.18",
|
||||||
"update-browserslist-db": "^1.1.0"
|
"update-browserslist-db": "^1.1.0"
|
||||||
},
|
},
|
||||||
|
|
@ -1614,9 +1614,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001667",
|
"version": "1.0.30001663",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz",
|
||||||
"integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==",
|
"integrity": "sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -2192,9 +2192,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.32",
|
"version": "1.5.28",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.28.tgz",
|
||||||
"integrity": "sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==",
|
"integrity": "sha512-VufdJl+rzaKZoYVUijN13QcXVF5dWPZANeFTLNy+OSpHdDL5ynXTF35+60RSBbaQYB1ae723lQXHCrf4pyLsMw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
|
@ -2452,7 +2452,6 @@
|
||||||
"version": "8.57.1",
|
"version": "8.57.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
|
||||||
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
|
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
|
||||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -2506,9 +2505,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-react": {
|
"node_modules/eslint-plugin-react": {
|
||||||
"version": "7.37.1",
|
"version": "7.36.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.36.1.tgz",
|
||||||
"integrity": "sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==",
|
"integrity": "sha512-/qwbqNXZoq+VP30s1d4Nc1C5GTxjJQjk4Jzs4Wq2qzxFM7dSmuG2UkIjg2USMLh3A/aVcUNrK7v0J5U1XEGGwA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -3066,21 +3065,6 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/fsevents": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
|
|
@ -3203,9 +3187,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globals": {
|
"node_modules/globals": {
|
||||||
"version": "15.10.0",
|
"version": "15.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz",
|
||||||
"integrity": "sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==",
|
"integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -3407,9 +3391,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/html-url-attributes": {
|
"node_modules/html-url-attributes": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz",
|
||||||
"integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
|
"integrity": "sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
|
@ -4246,8 +4230,7 @@
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
|
|
@ -4419,7 +4402,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -5614,9 +5596,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/openai": {
|
"node_modules/openai": {
|
||||||
"version": "4.67.1",
|
"version": "4.63.0",
|
||||||
"resolved": "https://registry.npmjs.org/openai/-/openai-4.67.1.tgz",
|
"resolved": "https://registry.npmjs.org/openai/-/openai-4.63.0.tgz",
|
||||||
"integrity": "sha512-2YbRFy6qaYRJabK2zLMn4txrB2xBy0KP5g/eoqeSPTT31mIJMnkT75toagvfE555IKa2RdrzJrZwdDsUipsAMw==",
|
"integrity": "sha512-Y9V4KODbmrOpqiOmCDVnPfMxMqKLOx8Hwcdn/r8mePq4yv7FSXGnxCs8/jZKO7zCB/IVPWihpJXwJNAIOEiZ2g==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.11.18",
|
||||||
|
|
@ -5640,9 +5622,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/openai/node_modules/@types/node": {
|
"node_modules/openai/node_modules/@types/node": {
|
||||||
"version": "18.19.54",
|
"version": "18.19.50",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.50.tgz",
|
||||||
"integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==",
|
"integrity": "sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~5.26.4"
|
||||||
|
|
@ -5826,9 +5808,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/package-json-from-dist": {
|
"node_modules/package-json-from-dist": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
||||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0"
|
"license": "BlueOak-1.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -6286,7 +6268,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
|
|
@ -6404,16 +6385,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/regexp.prototype.flags": {
|
"node_modules/regexp.prototype.flags": {
|
||||||
"version": "1.5.3",
|
"version": "1.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
|
||||||
"integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==",
|
"integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind": "^1.0.7",
|
"call-bind": "^1.0.6",
|
||||||
"define-properties": "^1.2.1",
|
"define-properties": "^1.2.1",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"set-function-name": "^2.0.2"
|
"set-function-name": "^2.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
|
|
@ -7540,15 +7521,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript-eslint": {
|
"node_modules/typescript-eslint": {
|
||||||
"version": "8.8.0",
|
"version": "8.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.7.0.tgz",
|
||||||
"integrity": "sha512-BjIT/VwJ8+0rVO01ZQ2ZVnjE1svFBiRczcpr1t1Yxt7sT25VSbPfrJtDsQ8uQTy2pilX5nI9gwxhUyLULNentw==",
|
"integrity": "sha512-nEHbEYJyHwsuf7c3V3RS7Saq+1+la3i0ieR3qP0yjqWSzVmh8Drp47uOl9LjbPANac4S7EFSqvcYIKXUUwIfIQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "8.8.0",
|
"@typescript-eslint/eslint-plugin": "8.7.0",
|
||||||
"@typescript-eslint/parser": "8.8.0",
|
"@typescript-eslint/parser": "8.7.0",
|
||||||
"@typescript-eslint/utils": "8.8.0"
|
"@typescript-eslint/utils": "8.7.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
|
@ -7679,9 +7660,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
|
||||||
"integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
|
"integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -7699,8 +7680,8 @@
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"escalade": "^3.2.0",
|
"escalade": "^3.1.2",
|
||||||
"picocolors": "^1.1.0"
|
"picocolors": "^1.0.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"update-browserslist-db": "cli.js"
|
"update-browserslist-db": "cli.js"
|
||||||
|
|
|
||||||
|
|
@ -19,25 +19,26 @@
|
||||||
"void.whichApi": {
|
"void.whichApi": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "anthropic",
|
"default": "anthropic",
|
||||||
"description": "Choose an API provider",
|
"description": "Choose an API provider.",
|
||||||
"enum": [
|
"enum": [
|
||||||
"openai",
|
"openAI",
|
||||||
|
"openRouter",
|
||||||
|
"openAICompatible",
|
||||||
"anthropic",
|
"anthropic",
|
||||||
"azure",
|
"azure",
|
||||||
"greptile",
|
|
||||||
"ollama",
|
"ollama",
|
||||||
"openaicompatible"
|
"greptile"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"void.anthropic.apiKey": {
|
"void.anthropic.apiKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Anthropic API Key"
|
"description": "Anthropic API key."
|
||||||
},
|
},
|
||||||
"void.anthropic.model": {
|
"void.anthropic.model": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "claude-3-5-sonnet-20240620",
|
"default": "claude-3-5-sonnet-20240620",
|
||||||
"description": "Anthropic Model to use.",
|
"description": "Anthropic model to use.",
|
||||||
"enum": [
|
"enum": [
|
||||||
"claude-3-5-sonnet-20240620",
|
"claude-3-5-sonnet-20240620",
|
||||||
"claude-3-opus-20240229",
|
"claude-3-opus-20240229",
|
||||||
|
|
@ -47,7 +48,7 @@
|
||||||
},
|
},
|
||||||
"void.anthropic.maxTokens": {
|
"void.anthropic.maxTokens": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "1024",
|
"default": "8192",
|
||||||
"description": "Anthropic max number of tokens to output.",
|
"description": "Anthropic max number of tokens to output.",
|
||||||
"enum": [
|
"enum": [
|
||||||
"1024",
|
"1024",
|
||||||
|
|
@ -59,12 +60,12 @@
|
||||||
"void.openAI.apiKey": {
|
"void.openAI.apiKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "OpenAI API Key."
|
"description": "OpenAI API key."
|
||||||
},
|
},
|
||||||
"void.openAI.model": {
|
"void.openAI.model": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "gpt-4o",
|
"default": "gpt-4o",
|
||||||
"description": "OpenAI model.",
|
"description": "OpenAI model to use.",
|
||||||
"enum": [
|
"enum": [
|
||||||
"o1-preview",
|
"o1-preview",
|
||||||
"o1-mini",
|
"o1-mini",
|
||||||
|
|
@ -88,12 +89,55 @@
|
||||||
"void.greptile.apiKey": {
|
"void.greptile.apiKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Greptile API Key."
|
"description": "Greptile API key."
|
||||||
},
|
},
|
||||||
"void.greptile.githubPAT": {
|
"void.greptile.githubPAT": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Github PAT given to Greptile to access your repository."
|
"description": "Github PAT given to Greptile to access your repository."
|
||||||
|
},
|
||||||
|
"void.greptile.remote": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "remote provider",
|
||||||
|
"enum": [
|
||||||
|
"github",
|
||||||
|
"gitlab"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"void.greptile.repository": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Repository identifier in \"owner/repository\" format."
|
||||||
|
},
|
||||||
|
"void.greptile.branch": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "main",
|
||||||
|
"description": "Name of the git branch."
|
||||||
|
},
|
||||||
|
"void.azure.apiKey": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Azure API key."
|
||||||
|
},
|
||||||
|
"void.azure.deploymentId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Azure API deployment ID."
|
||||||
|
},
|
||||||
|
"void.azure.resourceName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name of the Azure OpenAI resource. Either this or `baseURL` can be used. \nThe resource name is used in the assembled URL: `https://{resourceName}.openai.azure.com/openai/deployments/{modelId}{path}`"
|
||||||
|
},
|
||||||
|
"void.azure.providerSettings": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"baseURL": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "https://${resourceName}.openai.azure.com/openai/deployments",
|
||||||
|
"description": "Azure API base URL."
|
||||||
|
},
|
||||||
|
"headers": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Custom headers to include in the requests."
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"void.ollama.endpoint": {
|
"void.ollama.endpoint": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -193,20 +237,30 @@
|
||||||
"smollm:1.7b"
|
"smollm:1.7b"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"void.openaiCompatible.endpoint": {
|
"void.openRouter.model": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "openai/gpt-4o",
|
||||||
|
"description": "OpenRouter model to use."
|
||||||
|
},
|
||||||
|
"void.openRouter.apiKey": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"description": "OpenRouter API key."
|
||||||
|
},
|
||||||
|
"void.openAICompatible.endpoint": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "http://127.0.0.1:11434/v1",
|
"default": "http://127.0.0.1:11434/v1",
|
||||||
"description": "The openai compatible api provider's endpoint. default value is a example of the ollama openai-mode uri"
|
"description": "The endpoint."
|
||||||
},
|
},
|
||||||
"void.openaiCompatible.model": {
|
"void.openAICompatible.model": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "gpt-4o",
|
||||||
|
"description": "The name of the model to use."
|
||||||
|
},
|
||||||
|
"void.openAICompatible.apiKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Your provider's model name to use."
|
"description": "Your API key."
|
||||||
},
|
|
||||||
"void.openaiCompatible.apiKey": {
|
|
||||||
"type": "string",
|
|
||||||
"default": "",
|
|
||||||
"description": "Your provider's API Key."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -227,6 +281,16 @@
|
||||||
"command": "void.discardDiff",
|
"command": "void.discardDiff",
|
||||||
"title": "Discard Diff"
|
"title": "Discard Diff"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "void.startNewThread",
|
||||||
|
"title": "Start a new chat",
|
||||||
|
"icon": "$(add)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "void.toggleThreadSelector",
|
||||||
|
"title": "View past chats",
|
||||||
|
"icon": "$(history)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "void.openSettings",
|
"command": "void.openSettings",
|
||||||
"title": "Void settings",
|
"title": "Void settings",
|
||||||
|
|
@ -265,6 +329,16 @@
|
||||||
],
|
],
|
||||||
"menus": {
|
"menus": {
|
||||||
"view/title": [
|
"view/title": [
|
||||||
|
{
|
||||||
|
"command": "void.startNewThread",
|
||||||
|
"when": "view == 'void.viewnumberone'",
|
||||||
|
"group": "navigation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "void.toggleThreadSelector",
|
||||||
|
"when": "view == 'void.viewnumberone'",
|
||||||
|
"group": "navigation"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "void.openSettings",
|
"command": "void.openSettings",
|
||||||
"when": "view == 'void.viewnumberone'",
|
"when": "view == 'void.viewnumberone'",
|
||||||
|
|
@ -315,4 +389,4 @@
|
||||||
"openai": "^4.57.0",
|
"openai": "^4.57.0",
|
||||||
"diff": "^7.0.0"
|
"diff": "^7.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import * as vscode from 'vscode';
|
|
||||||
|
|
||||||
export class CtrlKCodeLensProvider implements vscode.CodeLensProvider {
|
|
||||||
|
|
||||||
private codelensesOfDocument: { [documentUri: string]: vscode.CodeLens[] } = {};
|
|
||||||
|
|
||||||
// only called by vscode's internals
|
|
||||||
public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens[]> {
|
|
||||||
const docUri = document.uri.toString()
|
|
||||||
return this.codelensesOfDocument[docUri];
|
|
||||||
}
|
|
||||||
|
|
||||||
// only called by us
|
|
||||||
public addNewCodeLens(document: vscode.TextDocument, selection: vscode.Selection) {
|
|
||||||
|
|
||||||
const docUri = document.uri.toString()
|
|
||||||
|
|
||||||
if (!this.codelensesOfDocument[docUri])
|
|
||||||
this.codelensesOfDocument[docUri] = []
|
|
||||||
|
|
||||||
// if any other codelens intersects with the selection, don't do it (and have the user now focus that codelens)
|
|
||||||
for (let lens of this.codelensesOfDocument[docUri]) {
|
|
||||||
if (lens.range.intersection(selection))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.codelensesOfDocument[docUri] = [
|
|
||||||
...this.codelensesOfDocument[docUri],
|
|
||||||
new vscode.CodeLens(new vscode.Range(selection.start.line, 0, selection.end.line, Infinity), { title: '', command: '' })];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -20,6 +20,7 @@ export class SidebarWebviewProvider implements vscode.WebviewViewProvider {
|
||||||
private readonly _extensionUri: vscode.Uri
|
private readonly _extensionUri: vscode.Uri
|
||||||
|
|
||||||
private _webviewView?: vscode.WebviewView; // only used inside onDidChangeConfiguration
|
private _webviewView?: vscode.WebviewView; // only used inside onDidChangeConfiguration
|
||||||
|
private _webviewDeps: string[] = [];
|
||||||
|
|
||||||
constructor(context: vscode.ExtensionContext) {
|
constructor(context: vscode.ExtensionContext) {
|
||||||
// const extensionPath = context.extensionPath // the directory where the extension is installed, might be useful later... was included in webviewProvider code
|
// const extensionPath = context.extensionPath // the directory where the extension is installed, might be useful later... was included in webviewProvider code
|
||||||
|
|
@ -30,8 +31,10 @@ export class SidebarWebviewProvider implements vscode.WebviewViewProvider {
|
||||||
if (!temp_res) throw new Error("sidebar provider: resolver was undefined")
|
if (!temp_res) throw new Error("sidebar provider: resolver was undefined")
|
||||||
this._res = temp_res
|
this._res = temp_res
|
||||||
|
|
||||||
|
// if it affects one of the config items webview depends on, update the webview
|
||||||
|
// TODO should be able to move this entirely to React - make updateWebviewHTML mount once, and then send updates via postMessage from then on
|
||||||
vscode.workspace.onDidChangeConfiguration(event => {
|
vscode.workspace.onDidChangeConfiguration(event => {
|
||||||
if (event.affectsConfiguration('void.ollama.endpoint')) {
|
if (this._webviewDeps.map(dep => event.affectsConfiguration(dep)).some(v => !!v)) {
|
||||||
if (this._webviewView) {
|
if (this._webviewView) {
|
||||||
this.updateWebviewHTML(this._webviewView.webview);
|
this.updateWebviewHTML(this._webviewView.webview);
|
||||||
}
|
}
|
||||||
|
|
@ -39,14 +42,20 @@ export class SidebarWebviewProvider implements vscode.WebviewViewProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is updated
|
||||||
private updateWebviewHTML(webview: vscode.Webview) {
|
private updateWebviewHTML(webview: vscode.Webview) {
|
||||||
const allowed_urls = ['https://api.anthropic.com', 'https://api.openai.com', 'https://api.greptile.com'];
|
const allowed_urls = ['https://api.anthropic.com', 'https://api.openai.com', 'https://api.greptile.com'];
|
||||||
const ollamaEndpoint: string | undefined = vscode.workspace.getConfiguration('void').get('ollama.endpoint');
|
this._webviewDeps = []
|
||||||
|
|
||||||
|
const ollamaEndpoint: string | undefined = vscode.workspace.getConfiguration('void.ollama').get('endpoint');
|
||||||
|
this._webviewDeps.push('void.ollama.endpoint');
|
||||||
if (ollamaEndpoint)
|
if (ollamaEndpoint)
|
||||||
allowed_urls.push(ollamaEndpoint);
|
allowed_urls.push(ollamaEndpoint);
|
||||||
const openaiCompatibleEndpoint: string | undefined = vscode.workspace.getConfiguration('void').get('openaiCompatible.endpoint');
|
|
||||||
if (openaiCompatibleEndpoint)
|
const openAICompatibleEndpoint: string | undefined = vscode.workspace.getConfiguration('void.openAICompatible').get('endpoint');
|
||||||
allowed_urls.push(openaiCompatibleEndpoint);
|
this._webviewDeps.push('void.openAICompatible.endpoint');
|
||||||
|
if (openAICompatibleEndpoint)
|
||||||
|
allowed_urls.push(openAICompatibleEndpoint);
|
||||||
|
|
||||||
const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist/sidebar/index.js'));
|
const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist/sidebar/index.js'));
|
||||||
const stylesUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist/sidebar/styles.css'));
|
const stylesUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist/sidebar/styles.css'));
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ export type ApiConfig = {
|
||||||
model: string;
|
model: string;
|
||||||
maxTokens: string;
|
maxTokens: string;
|
||||||
},
|
},
|
||||||
openai: {
|
openAI: {
|
||||||
apikey: string;
|
apikey: string,
|
||||||
model: string;
|
model: string,
|
||||||
},
|
},
|
||||||
greptile: {
|
greptile: {
|
||||||
apikey: string;
|
apikey: string;
|
||||||
|
|
@ -26,12 +26,16 @@ export type ApiConfig = {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
model: string;
|
model: string;
|
||||||
},
|
},
|
||||||
openaicompatible: {
|
openAICompatible: {
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
model: string,
|
model: string,
|
||||||
apikey: string
|
apikey: string
|
||||||
},
|
},
|
||||||
whichApi: string;
|
openRouter: {
|
||||||
|
model: string,
|
||||||
|
apikey: string
|
||||||
|
}
|
||||||
|
whichApi: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type OnText = (newText: string, fullText: string) => void;
|
type OnText = (newText: string, fullText: string) => void;
|
||||||
|
|
@ -136,7 +140,8 @@ const sendClaudeMsg: SendLLMMessageFnTypeInternal = ({
|
||||||
return { abort };
|
return { abort };
|
||||||
};
|
};
|
||||||
|
|
||||||
// OpenAI
|
|
||||||
|
// OpenAI, OpenRouter, OpenAICompatible
|
||||||
const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({
|
const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({
|
||||||
messages,
|
messages,
|
||||||
onText,
|
onText,
|
||||||
|
|
@ -144,7 +149,7 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({
|
||||||
onError,
|
onError,
|
||||||
apiConfig,
|
apiConfig,
|
||||||
}) => {
|
}) => {
|
||||||
const { apikey, model } = apiConfig.openai;
|
const { apikey, model } = apiConfig.openAI;
|
||||||
|
|
||||||
if (!apikey) {
|
if (!apikey) {
|
||||||
return handleMissingApiKey('OpenAI', onError);
|
return handleMissingApiKey('OpenAI', onError);
|
||||||
|
|
@ -153,21 +158,39 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({
|
||||||
let didAbort = false;
|
let didAbort = false;
|
||||||
let fullText = '';
|
let fullText = '';
|
||||||
|
|
||||||
const openai = new OpenAI({
|
|
||||||
apiKey: apikey,
|
|
||||||
dangerouslyAllowBrowser: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
let abort = () => {
|
let abort = () => {
|
||||||
didAbort = true;
|
didAbort = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let openai: OpenAI
|
||||||
|
let options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming
|
||||||
|
|
||||||
|
|
||||||
|
if (apiConfig.whichApi === 'openAI') {
|
||||||
|
openai = new OpenAI({ apiKey: apiConfig.openAI.apikey, dangerouslyAllowBrowser: true });
|
||||||
|
options = { model: apiConfig.openAI.model, messages: messages, stream: true, }
|
||||||
|
}
|
||||||
|
else if (apiConfig.whichApi === 'openRouter') {
|
||||||
|
openai = new OpenAI({
|
||||||
|
baseURL: "https://openrouter.ai/api/v1", apiKey: apiConfig.openRouter.apikey, dangerouslyAllowBrowser: true,
|
||||||
|
defaultHeaders: {
|
||||||
|
"HTTP-Referer": 'https://voideditor.com', // Optional, for including your app on openrouter.ai rankings.
|
||||||
|
"X-Title": 'Void Editor', // Optional. Shows in rankings on openrouter.ai.
|
||||||
|
},
|
||||||
|
});
|
||||||
|
options = { model: apiConfig.openRouter.model, messages: messages, stream: true, }
|
||||||
|
}
|
||||||
|
else if (apiConfig.whichApi === 'openAICompatible') {
|
||||||
|
openai = new OpenAI({ baseURL: apiConfig.openAICompatible.endpoint, apiKey: apiConfig.openAICompatible.apikey, dangerouslyAllowBrowser: true })
|
||||||
|
options = { model: apiConfig.openAICompatible.model, messages: messages, stream: true, }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error(`sendOpenAIMsg: invalid whichApi: ${apiConfig.whichApi}`)
|
||||||
|
throw new Error(`apiConfig.whichAPI was invalid: ${apiConfig.whichApi}`)
|
||||||
|
}
|
||||||
|
|
||||||
openai.chat.completions
|
openai.chat.completions
|
||||||
.create({
|
.create(options)
|
||||||
model: model,
|
|
||||||
messages: messages,
|
|
||||||
stream: true,
|
|
||||||
})
|
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
abort = () => {
|
abort = () => {
|
||||||
response.controller.abort();
|
response.controller.abort();
|
||||||
|
|
@ -204,56 +227,6 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({
|
||||||
return { abort };
|
return { abort };
|
||||||
};
|
};
|
||||||
|
|
||||||
// OpenAI Compatible
|
|
||||||
const sendOpenAICompatibleMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, apiConfig }) => {
|
|
||||||
|
|
||||||
let didAbort = false
|
|
||||||
let fullText = ''
|
|
||||||
|
|
||||||
// if abort is called, onFinalMessage is NOT called, and no later onTexts are called either
|
|
||||||
let abort: () => void = () => {
|
|
||||||
didAbort = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const openai = new OpenAI({ apiKey: apiConfig.openaicompatible.apikey, baseURL: apiConfig.openaicompatible.endpoint, dangerouslyAllowBrowser: true });
|
|
||||||
|
|
||||||
openai.chat.completions.create({
|
|
||||||
model: apiConfig.openaicompatible.model,
|
|
||||||
messages: messages,
|
|
||||||
stream: true,
|
|
||||||
})
|
|
||||||
.then(async response => {
|
|
||||||
abort = () => {
|
|
||||||
response.controller.abort()
|
|
||||||
didAbort = true;
|
|
||||||
}
|
|
||||||
// when receive text
|
|
||||||
try {
|
|
||||||
for await (const chunk of response) {
|
|
||||||
if (didAbort) return;
|
|
||||||
const newText = chunk.choices[0]?.delta?.content || '';
|
|
||||||
fullText += newText;
|
|
||||||
onText(newText, fullText);
|
|
||||||
}
|
|
||||||
onFinalMessage(fullText);
|
|
||||||
}
|
|
||||||
// when error/fail
|
|
||||||
catch (error) {
|
|
||||||
onError(`Error in OpenAI stream:, ${error}`)
|
|
||||||
console.error('Error in OpenAI stream:', error);
|
|
||||||
onFinalMessage(fullText);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((responseError) => {
|
|
||||||
if (responseError.status === 401) {
|
|
||||||
onError('Unauthorized: Invalid API key');
|
|
||||||
} else {
|
|
||||||
onError(responseError.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { abort };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ollama
|
// Ollama
|
||||||
const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({
|
const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({
|
||||||
messages,
|
messages,
|
||||||
|
|
@ -425,6 +398,7 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({
|
||||||
|
|
||||||
switch (apiConfig.whichApi) {
|
switch (apiConfig.whichApi) {
|
||||||
case 'anthropic':
|
case 'anthropic':
|
||||||
|
|
||||||
return sendClaudeMsg({
|
return sendClaudeMsg({
|
||||||
messages,
|
messages,
|
||||||
onText,
|
onText,
|
||||||
|
|
@ -432,14 +406,10 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({
|
||||||
onError,
|
onError,
|
||||||
apiConfig,
|
apiConfig,
|
||||||
});
|
});
|
||||||
case 'openai':
|
case 'openAI':
|
||||||
return sendOpenAIMsg({
|
case 'openRouter':
|
||||||
messages,
|
case 'openAICompatible':
|
||||||
onText,
|
return sendOpenAIMsg({ messages, onText, onFinalMessage, onError, apiConfig });
|
||||||
onFinalMessage,
|
|
||||||
onError,
|
|
||||||
apiConfig,
|
|
||||||
});
|
|
||||||
case 'greptile':
|
case 'greptile':
|
||||||
return sendGreptileMsg({
|
return sendGreptileMsg({
|
||||||
messages,
|
messages,
|
||||||
|
|
@ -449,6 +419,7 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({
|
||||||
apiConfig,
|
apiConfig,
|
||||||
});
|
});
|
||||||
case 'ollama':
|
case 'ollama':
|
||||||
|
|
||||||
return sendOllamaMsg({
|
return sendOllamaMsg({
|
||||||
messages,
|
messages,
|
||||||
onText,
|
onText,
|
||||||
|
|
@ -456,16 +427,9 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({
|
||||||
onError,
|
onError,
|
||||||
apiConfig,
|
apiConfig,
|
||||||
});
|
});
|
||||||
case 'openaicompatible':
|
|
||||||
return sendOpenAICompatibleMsg({
|
|
||||||
messages,
|
|
||||||
onText,
|
|
||||||
onFinalMessage,
|
|
||||||
onError,
|
|
||||||
apiConfig,
|
|
||||||
});
|
|
||||||
default:
|
default:
|
||||||
onError(`Error: whichApi was '${apiConfig.whichApi}', which is not recognized!`);
|
onError(`Error: whichApi was '${apiConfig.whichApi}', which is not recognized!`);
|
||||||
return { abort: () => {} };
|
return { abort: () => {} };
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { WebviewMessage } from './shared_types';
|
import { ChatThreads, WebviewMessage } from './shared_types';
|
||||||
import { CtrlKCodeLensProvider } from './CtrlKCodeLensProvider';
|
|
||||||
import { getDiffedLines } from './getDiffedLines';
|
import { getDiffedLines } from './getDiffedLines';
|
||||||
import { ApprovalCodeLensProvider } from './ApprovalCodeLensProvider';
|
import { ApprovalCodeLensProvider } from './ApprovalCodeLensProvider';
|
||||||
import { SidebarWebviewProvider } from './SidebarWebviewProvider';
|
import { SidebarWebviewProvider } from './SidebarWebviewProvider';
|
||||||
|
|
@ -18,7 +17,7 @@ const getApiConfig = () => {
|
||||||
model: vscode.workspace.getConfiguration('void.anthropic').get('model') ?? '',
|
model: vscode.workspace.getConfiguration('void.anthropic').get('model') ?? '',
|
||||||
maxTokens: vscode.workspace.getConfiguration('void.anthropic').get('maxTokens') ?? '',
|
maxTokens: vscode.workspace.getConfiguration('void.anthropic').get('maxTokens') ?? '',
|
||||||
},
|
},
|
||||||
openai: {
|
openAI: {
|
||||||
apikey: vscode.workspace.getConfiguration('void.openAI').get('apiKey') ?? '',
|
apikey: vscode.workspace.getConfiguration('void.openAI').get('apiKey') ?? '',
|
||||||
model: vscode.workspace.getConfiguration('void.openAI').get('model') ?? '',
|
model: vscode.workspace.getConfiguration('void.openAI').get('model') ?? '',
|
||||||
},
|
},
|
||||||
|
|
@ -35,10 +34,14 @@ const getApiConfig = () => {
|
||||||
endpoint: vscode.workspace.getConfiguration('void.ollama').get('endpoint') ?? '',
|
endpoint: vscode.workspace.getConfiguration('void.ollama').get('endpoint') ?? '',
|
||||||
model: vscode.workspace.getConfiguration('void.ollama').get('model') ?? '',
|
model: vscode.workspace.getConfiguration('void.ollama').get('model') ?? '',
|
||||||
},
|
},
|
||||||
openaicompatible: {
|
openAICompatible: {
|
||||||
endpoint: vscode.workspace.getConfiguration('void.openaiCompatible').get('endpoint') ?? '',
|
endpoint: vscode.workspace.getConfiguration('void.openAICompatible').get('endpoint') ?? '',
|
||||||
apikey: vscode.workspace.getConfiguration('void.openaiCompatible').get('apiKey') ?? '',
|
model: vscode.workspace.getConfiguration('void.openAICompatible').get('model') ?? '',
|
||||||
model: vscode.workspace.getConfiguration('void.openaiCompatible').get('model') ?? '',
|
apikey: vscode.workspace.getConfiguration('void.openAICompatible').get('apiKey') ?? '',
|
||||||
|
},
|
||||||
|
openRouter: {
|
||||||
|
model: vscode.workspace.getConfiguration('void.openRouter').get('model') ?? '',
|
||||||
|
apikey: vscode.workspace.getConfiguration('void.openRouter').get('apiKey') ?? '',
|
||||||
},
|
},
|
||||||
whichApi: vscode.workspace.getConfiguration('void').get('whichApi') ?? ''
|
whichApi: vscode.workspace.getConfiguration('void').get('whichApi') ?? ''
|
||||||
}
|
}
|
||||||
|
|
@ -100,6 +103,14 @@ export function activate(context: vscode.ExtensionContext) {
|
||||||
webviewProvider.webview.then(
|
webviewProvider.webview.then(
|
||||||
webview => {
|
webview => {
|
||||||
|
|
||||||
|
// top navigation bar commands
|
||||||
|
context.subscriptions.push(vscode.commands.registerCommand('void.startNewThread', async () => {
|
||||||
|
webview.postMessage({ type: 'startNewThread' } satisfies WebviewMessage)
|
||||||
|
}))
|
||||||
|
context.subscriptions.push(vscode.commands.registerCommand('void.toggleThreadSelector', async () => {
|
||||||
|
webview.postMessage({ type: 'toggleThreadSelector' } satisfies WebviewMessage)
|
||||||
|
}))
|
||||||
|
|
||||||
// when config changes, send it to the sidebar
|
// when config changes, send it to the sidebar
|
||||||
vscode.workspace.onDidChangeConfiguration(e => {
|
vscode.workspace.onDidChangeConfiguration(e => {
|
||||||
if (e.affectsConfiguration('void')) {
|
if (e.affectsConfiguration('void')) {
|
||||||
|
|
@ -135,12 +146,17 @@ export function activate(context: vscode.ExtensionContext) {
|
||||||
await approvalCodeLensProvider.addNewApprovals(editor, suggestedEdits)
|
await approvalCodeLensProvider.addNewApprovals(editor, suggestedEdits)
|
||||||
}
|
}
|
||||||
else if (m.type === 'getApiConfig') {
|
else if (m.type === 'getApiConfig') {
|
||||||
|
|
||||||
const apiConfig = getApiConfig()
|
const apiConfig = getApiConfig()
|
||||||
console.log('Api config:', apiConfig)
|
|
||||||
|
|
||||||
webview.postMessage({ type: 'apiConfig', apiConfig } satisfies WebviewMessage)
|
webview.postMessage({ type: 'apiConfig', apiConfig } satisfies WebviewMessage)
|
||||||
|
}
|
||||||
|
else if (m.type === 'getAllThreads') {
|
||||||
|
const threads: ChatThreads = context.workspaceState.get('allThreads') ?? {}
|
||||||
|
webview.postMessage({ type: 'allThreads', threads } satisfies WebviewMessage)
|
||||||
|
}
|
||||||
|
else if (m.type === 'persistThread') {
|
||||||
|
const threads: ChatThreads = context.workspaceState.get('allThreads') ?? {}
|
||||||
|
const updatedThreads: ChatThreads = { ...threads, [m.thread.id]: m.thread }
|
||||||
|
context.workspaceState.update('allThreads', updatedThreads)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.error('unrecognized command', m.type, m)
|
console.error('unrecognized command', m.type, m)
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,52 @@ type WebviewMessage = (
|
||||||
// editor -> sidebar
|
// editor -> sidebar
|
||||||
| { type: 'apiConfig', apiConfig: ApiConfig }
|
| { type: 'apiConfig', apiConfig: ApiConfig }
|
||||||
|
|
||||||
|
// sidebar -> editor
|
||||||
|
| { type: 'getAllThreads' }
|
||||||
|
|
||||||
|
// editor -> sidebar
|
||||||
|
| { type: 'allThreads', threads: ChatThreads }
|
||||||
|
|
||||||
|
// sidebar -> editor
|
||||||
|
| { type: 'persistThread', thread: ChatThreads[string] }
|
||||||
|
|
||||||
|
// editor -> sidebar
|
||||||
|
| { type: 'startNewThread' }
|
||||||
|
|
||||||
|
// editor -> sidebar
|
||||||
|
| { type: 'toggleThreadSelector' }
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Command = WebviewMessage['type']
|
type Command = WebviewMessage['type']
|
||||||
|
|
||||||
|
type ChatThreads = {
|
||||||
|
[id: string]: {
|
||||||
|
id: string; // store the id here too
|
||||||
|
createdAt: string;
|
||||||
|
messages: ChatMessage[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatMessage =
|
||||||
|
| {
|
||||||
|
role: "user";
|
||||||
|
content: string; // content sent to the llm
|
||||||
|
displayContent: string; // content displayed to user
|
||||||
|
selection: Selection | null; // the user's selection
|
||||||
|
files: vscode.Uri[]; // the files sent in the message
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
role: "assistant";
|
||||||
|
content: string; // content received from LLM
|
||||||
|
displayContent: string; // content displayed to user (this is the same as content for now)
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Selection,
|
Selection,
|
||||||
File,
|
File,
|
||||||
WebviewMessage,
|
WebviewMessage,
|
||||||
Command,
|
Command,
|
||||||
|
ChatThreads,
|
||||||
|
ChatMessage,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
import React, { JSX, useState } from 'react';
|
|
||||||
import { MarkedToken, Token, TokensList } from 'marked';
|
|
||||||
import { awaitVSCodeResponse, getVSCodeAPI } from './getVscodeApi';
|
|
||||||
|
|
||||||
|
|
||||||
// code block with Apply button at top
|
|
||||||
export const BlockCode = ({ text, disableApplyButton = false }: { text: string, disableApplyButton?: boolean }) => {
|
|
||||||
return <div className='py-1'>
|
|
||||||
{disableApplyButton ? null : <div className='text-sm'>
|
|
||||||
<button className='btn btn-secondary px-3 py-1 text-sm rounded-t-sm'
|
|
||||||
onClick={async () => { getVSCodeAPI().postMessage({ type: 'applyCode', code: text }) }}>Apply</button>
|
|
||||||
</div>}
|
|
||||||
<div className={`overflow-x-auto rounded-sm text-vscode-editor-fg bg-vscode-editor-bg ${disableApplyButton ? '' : 'rounded-tl-none'}`}>
|
|
||||||
<pre className='p-3'>
|
|
||||||
{text}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
const Render = ({ token }: { token: Token }) => {
|
|
||||||
|
|
||||||
// deal with built-in tokens first (assume marked token)
|
|
||||||
const t = token as MarkedToken
|
|
||||||
|
|
||||||
if (t.type === "space") {
|
|
||||||
return <span>{t.raw}</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "code") {
|
|
||||||
return <BlockCode text={t.text} />
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "heading") {
|
|
||||||
const HeadingTag = `h${t.depth}` as keyof JSX.IntrinsicElements;
|
|
||||||
return <HeadingTag>{t.text}</HeadingTag>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "table") {
|
|
||||||
return (
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{t.header.map((cell: any, index: number) => (
|
|
||||||
<th key={index} style={{ textAlign: t.align[index] || 'left' }}>
|
|
||||||
{cell.raw}
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{t.rows.map((row: any[], rowIndex: number) => (
|
|
||||||
<tr key={rowIndex}>
|
|
||||||
{row.map((cell: any, cellIndex: number) => (
|
|
||||||
<td key={cellIndex} style={{ textAlign: t.align[cellIndex] || 'left' }}>
|
|
||||||
{cell.raw}
|
|
||||||
</td>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "hr") {
|
|
||||||
return <hr />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "blockquote") {
|
|
||||||
return <blockquote>{t.text}</blockquote>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "list") {
|
|
||||||
|
|
||||||
const ListTag = t.ordered ? 'ol' : 'ul';
|
|
||||||
return (
|
|
||||||
<ListTag start={t.start !== '' ? t.start : undefined}
|
|
||||||
className={`list-inside ${t.ordered ? 'list-decimal' : 'list-disc'}`}
|
|
||||||
>
|
|
||||||
{t.items.map((item, index) => (
|
|
||||||
<li key={index}>
|
|
||||||
{item.task && (
|
|
||||||
<input type="checkbox" checked={item.checked} readOnly />
|
|
||||||
)}
|
|
||||||
{item.text}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ListTag>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "paragraph") {
|
|
||||||
return <p>
|
|
||||||
{t.tokens.map((token, index) => (
|
|
||||||
<Render key={index} token={token} />
|
|
||||||
))}
|
|
||||||
</p>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "html") {
|
|
||||||
return <pre>{`<html>`}{t.raw}{`</html>`}</pre>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "text" || t.type === "escape") {
|
|
||||||
return <span>{t.raw}</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "def") {
|
|
||||||
return null; // Definitions are typically not rendered
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "link") {
|
|
||||||
return <a href={t.href} title={t.title ?? undefined}>{t.text}</a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "image") {
|
|
||||||
return <img src={t.href} alt={t.text} title={t.title ?? undefined} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "strong") {
|
|
||||||
return <strong>{t.text}</strong>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "em") {
|
|
||||||
return <em>{t.text}</em>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// inline code
|
|
||||||
if (t.type === "codespan") {
|
|
||||||
return <code className='text-vscode-editor-fg bg-vscode-editor-bg px-1 rounded-sm font-mono'>{t.text}</code>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "br") {
|
|
||||||
return <br />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.type === "del") {
|
|
||||||
return <del>{t.text}</del>;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// default
|
|
||||||
return <div className='bg-orange-50 rounded-sm overflow-hidden'>
|
|
||||||
<span className='text-xs text-orange-500'>Unknown type:</span>
|
|
||||||
{t.raw}
|
|
||||||
</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const MarkdownRender = ({ tokens }: { tokens: TokensList }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{tokens.map((token, index) => (
|
|
||||||
<Render key={index} token={token} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MarkdownRender;
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
import React, { useState, ChangeEvent, useEffect, useRef, useCallback, FormEvent } from "react"
|
import React, { useState, useEffect, useRef, useCallback, FormEvent } from "react"
|
||||||
import { ApiConfig, LLMMessage, sendLLMMessage } from "../common/sendLLMMessage"
|
import { ApiConfig, sendLLMMessage } from "../common/sendLLMMessage"
|
||||||
import { Command, File, Selection, WebviewMessage } from "../shared_types"
|
import { ChatMessage, File, Selection, WebviewMessage } from "../shared_types"
|
||||||
import { awaitVSCodeResponse, getVSCodeAPI, resolveAwaitingVSCodeResponse } from "./getVscodeApi"
|
import { awaitVSCodeResponse, getVSCodeAPI, resolveAwaitingVSCodeResponse } from "./getVscodeApi"
|
||||||
|
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
import MarkdownRender, { BlockCode } from "./MarkdownRender";
|
import MarkdownRender from "./markdown/MarkdownRender";
|
||||||
|
import BlockCode from "./markdown/BlockCode";
|
||||||
|
|
||||||
import * as vscode from 'vscode'
|
import * as vscode from 'vscode'
|
||||||
|
import { SelectedFiles } from "./components/SelectedFiles";
|
||||||
|
import { useThreads } from "./threadsContext";
|
||||||
|
|
||||||
|
|
||||||
const filesStr = (fullFiles: File[]) => {
|
const filesStr = (fullFiles: File[]) => {
|
||||||
|
|
@ -35,40 +38,6 @@ If you make a change, rewrite the entire file.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const FilesSelector = ({ files, setFiles }: { files: vscode.Uri[], setFiles: (files: vscode.Uri[]) => void }) => {
|
|
||||||
return files.length !== 0 && <div className='my-2'>
|
|
||||||
Include files:
|
|
||||||
{files.map((filename, i) =>
|
|
||||||
<div key={i} className='flex'>
|
|
||||||
{/* X button on a file */}
|
|
||||||
<button type='button' onClick={() => {
|
|
||||||
let file_index = files.indexOf(filename)
|
|
||||||
setFiles([...files.slice(0, file_index), ...files.slice(file_index + 1, Infinity)])
|
|
||||||
}}>
|
|
||||||
-{' '}<span className='text-gray-500'>{getBasename(filename.fsPath)}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
const IncludedFiles = ({ files }: { files: vscode.Uri[] }) => {
|
|
||||||
return files.length !== 0 && <div className='text-xs my-2'>
|
|
||||||
{files.map((filename, i) =>
|
|
||||||
<div key={i} className='flex'>
|
|
||||||
<button type='button'
|
|
||||||
className='btn btn-secondary pointer-events-none'
|
|
||||||
onClick={() => {
|
|
||||||
// TODO redirect to the document filename.fsPath, when add this remove pointer-events-none
|
|
||||||
}}>
|
|
||||||
-{' '}<span className='text-gray-100'>{getBasename(filename.fsPath)}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
||||||
|
|
||||||
const role = chatMessage.role
|
const role = chatMessage.role
|
||||||
|
|
@ -81,14 +50,14 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
||||||
|
|
||||||
if (role === 'user') {
|
if (role === 'user') {
|
||||||
chatbubbleContents = <>
|
chatbubbleContents = <>
|
||||||
<IncludedFiles files={chatMessage.files} />
|
<SelectedFiles files={chatMessage.files} setFiles={null} />
|
||||||
{chatMessage.selection?.selectionStr && <BlockCode text={chatMessage.selection.selectionStr} disableApplyButton={true} />}
|
{chatMessage.selection?.selectionStr && <BlockCode text={chatMessage.selection.selectionStr} hideToolbar />}
|
||||||
{children}
|
{children}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
else if (role === 'assistant') {
|
else if (role === 'assistant') {
|
||||||
const tokens = marked.lexer(children); // https://marked.js.org/using_pro#renderer
|
|
||||||
chatbubbleContents = <MarkdownRender tokens={tokens} /> // sectionsHTML
|
chatbubbleContents = <MarkdownRender string={children} /> // sectionsHTML
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -99,41 +68,48 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBasename = (pathStr: string) => {
|
const ThreadSelector = ({ onClose }: { onClose: () => void }) => {
|
||||||
// "unixify" path
|
const { allThreads, currentThread, switchToThread } = useThreads()
|
||||||
pathStr = pathStr.replace(/[/\\]+/g, '/'); // replace any / or \ or \\ with /
|
return (
|
||||||
const parts = pathStr.split('/') // split on /
|
<div className="flex flex-col space-y-1">
|
||||||
return parts[parts.length - 1]
|
<div className="text-right">
|
||||||
}
|
<button className="btn btn-sm" onClick={onClose}>
|
||||||
|
<svg
|
||||||
type ChatMessage = {
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
role: 'user'
|
fill="none"
|
||||||
content: string, // content sent to the llm
|
viewBox="0 0 24 24"
|
||||||
displayContent: string, // content displayed to user
|
stroke="currentColor"
|
||||||
selection: Selection | null, // the user's selection
|
className="size-4"
|
||||||
files: vscode.Uri[], // the files sent in the message
|
>
|
||||||
} | {
|
<path
|
||||||
role: 'assistant',
|
strokeLinecap="round"
|
||||||
content: string, // content received from LLM
|
strokeLinejoin="round"
|
||||||
displayContent: string // content displayed to user (this is the same as content for now)
|
d="M6 18 18 6M6 6l12 12"
|
||||||
}
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
// const [stateRef, setState] = useInstantState(initVal)
|
</div>
|
||||||
// setState instantly changes the value of stateRef instead of having to wait until the next render
|
{/* iterate through all past threads */}
|
||||||
const useInstantState = <T,>(initVal: T) => {
|
{Object.keys(allThreads ?? {}).map((threadId) => {
|
||||||
const stateRef = useRef<T>(initVal)
|
const pastThread = (allThreads ?? {})[threadId];
|
||||||
const [_, setS] = useState<T>(initVal)
|
return (
|
||||||
const setState = useCallback((newVal: T) => {
|
<button
|
||||||
setS(newVal);
|
key={pastThread.id}
|
||||||
stateRef.current = newVal;
|
className={`btn btn-sm btn-secondary ${pastThread.id === currentThread?.id ? "btn-primary" : ""}`}
|
||||||
}, [])
|
onClick={() => switchToThread(pastThread.id)}
|
||||||
return [stateRef as React.RefObject<T>, setState] as const // make s.current readonly - setState handles all changes
|
>
|
||||||
|
{new Date(pastThread.createdAt).toLocaleString()}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
|
const { allThreads, currentThread, addMessageToHistory, startNewThread, } = useThreads()
|
||||||
|
|
||||||
// state of current message
|
// state of current message
|
||||||
const [selection, setSelection] = useState<Selection | null>(null) // the code the user is selecting
|
const [selection, setSelection] = useState<Selection | null>(null) // the code the user is selecting
|
||||||
|
|
@ -141,13 +117,11 @@ const Sidebar = () => {
|
||||||
const [instructions, setInstructions] = useState('') // the user's instructions
|
const [instructions, setInstructions] = useState('') // the user's instructions
|
||||||
|
|
||||||
// state of chat
|
// state of chat
|
||||||
const [chatMessageHistory, setChatMessageHistory] = useState<ChatMessage[]>([])
|
|
||||||
const [messageStream, setMessageStream] = useState('')
|
const [messageStream, setMessageStream] = useState('')
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [requestFailed, setRequestFailed] = useState(false)
|
const [requestFailed, setRequestFailed] = useState(false)
|
||||||
const [requestFailedReason, setRequestFailedReason] = useState('')
|
const [requestFailedReason, setRequestFailedReason] = useState('')
|
||||||
|
const [isThreadSelectorOpen, setIsThreadSelectorOpen] = useState(false)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const abortFnRef = useRef<(() => void) | null>(null)
|
const abortFnRef = useRef<(() => void) | null>(null)
|
||||||
|
|
@ -170,13 +144,12 @@ const Sidebar = () => {
|
||||||
|
|
||||||
// if user pressed ctrl+l, add their selection to the sidebar
|
// if user pressed ctrl+l, add their selection to the sidebar
|
||||||
if (m.type === 'ctrl+l') {
|
if (m.type === 'ctrl+l') {
|
||||||
|
|
||||||
setSelection(m.selection)
|
setSelection(m.selection)
|
||||||
|
|
||||||
const filepath = m.selection.filePath
|
const filepath = m.selection.filePath
|
||||||
|
|
||||||
// add file if it's not a duplicate
|
// add current file to the context if it's not already in the files array
|
||||||
if (!files.find(f => f.fsPath === filepath.fsPath)) setFiles(files => [...files, filepath])
|
if (!files.find(f => f.fsPath === filepath.fsPath))
|
||||||
|
setFiles(files => [...files, filepath])
|
||||||
|
|
||||||
}
|
}
|
||||||
// when get apiConfig, set
|
// when get apiConfig, set
|
||||||
|
|
@ -184,10 +157,22 @@ const Sidebar = () => {
|
||||||
setApiConfig(m.apiConfig)
|
setApiConfig(m.apiConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if they pressed the + to add a new chat
|
||||||
|
else if (m.type === 'startNewThread') {
|
||||||
|
setIsThreadSelectorOpen(false)
|
||||||
|
if (currentThread?.messages.length !== 0)
|
||||||
|
startNewThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
// if they opened thread selector
|
||||||
|
else if (m.type === 'toggleThreadSelector') {
|
||||||
|
setIsThreadSelectorOpen(v => !v)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
window.addEventListener('message', listener);
|
window.addEventListener('message', listener);
|
||||||
return () => { window.removeEventListener('message', listener) }
|
return () => { window.removeEventListener('message', listener) }
|
||||||
}, [files, selection])
|
}, [files, selection, startNewThread, currentThread])
|
||||||
|
|
||||||
|
|
||||||
const formRef = useRef<HTMLFormElement | null>(null)
|
const formRef = useRef<HTMLFormElement | null>(null)
|
||||||
|
|
@ -206,15 +191,6 @@ const Sidebar = () => {
|
||||||
setSelection(null)
|
setSelection(null)
|
||||||
setFiles([])
|
setFiles([])
|
||||||
|
|
||||||
|
|
||||||
// TODO this is just a hack, turn this into a button instead, and track all histories somewhere
|
|
||||||
if (instructions === 'clear') {
|
|
||||||
setChatMessageHistory([])
|
|
||||||
setMessageStream('')
|
|
||||||
setIsLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// request file content from vscode and await response
|
// request file content from vscode and await response
|
||||||
getVSCodeAPI().postMessage({ type: 'requestFiles', filepaths: files })
|
getVSCodeAPI().postMessage({ type: 'requestFiles', filepaths: files })
|
||||||
const relevantFiles = await awaitVSCodeResponse('files')
|
const relevantFiles = await awaitVSCodeResponse('files')
|
||||||
|
|
@ -223,16 +199,18 @@ const Sidebar = () => {
|
||||||
const content = userInstructionsStr(instructions, relevantFiles.files, selection)
|
const content = userInstructionsStr(instructions, relevantFiles.files, selection)
|
||||||
// console.log('prompt:\n', content)
|
// console.log('prompt:\n', content)
|
||||||
const newHistoryElt: ChatMessage = { role: 'user', content, displayContent: instructions, selection, files }
|
const newHistoryElt: ChatMessage = { role: 'user', content, displayContent: instructions, selection, files }
|
||||||
setChatMessageHistory(chatMessageHistory => [...chatMessageHistory, newHistoryElt])
|
addMessageToHistory(newHistoryElt)
|
||||||
|
|
||||||
// send message to LLM
|
// send message to LLM
|
||||||
let { abort } = sendLLMMessage({
|
let { abort } = sendLLMMessage({
|
||||||
messages: [...chatMessageHistory.map(m => ({ role: m.role, content: m.content })), { role: 'user', content }],
|
messages: [...(currentThread?.messages ?? []).map(m => ({ role: m.role, content: m.content })), { role: 'user', content }],
|
||||||
onText: (newText, fullText) => setMessageStream(fullText),
|
onText: (newText, fullText) => setMessageStream(fullText),
|
||||||
onFinalMessage: (content) => {
|
onFinalMessage: (content) => {
|
||||||
// add assistant's message to chat history, and clear selection
|
// add assistant's message to chat history, and clear selection
|
||||||
const newHistoryElt: ChatMessage = { role: 'assistant', content, displayContent: content, }
|
const newHistoryElt: ChatMessage = { role: 'assistant', content, displayContent: content, }
|
||||||
setChatMessageHistory(chatMessageHistory => [...chatMessageHistory, newHistoryElt])
|
addMessageToHistory(newHistoryElt)
|
||||||
|
|
||||||
|
// clear selection
|
||||||
setMessageStream('')
|
setMessageStream('')
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
},
|
},
|
||||||
|
|
@ -250,12 +228,12 @@ const Sidebar = () => {
|
||||||
// if messageStream was not empty, add it to the history
|
// if messageStream was not empty, add it to the history
|
||||||
const llmContent = messageStream || '(canceled)'
|
const llmContent = messageStream || '(canceled)'
|
||||||
const newHistoryElt: ChatMessage = { role: 'assistant', displayContent: messageStream, content: llmContent }
|
const newHistoryElt: ChatMessage = { role: 'assistant', displayContent: messageStream, content: llmContent }
|
||||||
setChatMessageHistory(chatMessageHistory => [...chatMessageHistory, newHistoryElt])
|
addMessageToHistory(newHistoryElt)
|
||||||
|
|
||||||
setMessageStream('')
|
setMessageStream('')
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
|
|
||||||
}, [messageStream])
|
}, [addMessageToHistory, messageStream])
|
||||||
|
|
||||||
//Clear code selection
|
//Clear code selection
|
||||||
const clearSelection = () => {
|
const clearSelection = () => {
|
||||||
|
|
@ -264,9 +242,14 @@ const Sidebar = () => {
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className="flex flex-col h-screen w-full">
|
<div className="flex flex-col h-screen w-full">
|
||||||
|
{isThreadSelectorOpen && (
|
||||||
|
<div className="mb-2 max-h-[30vh] overflow-y-auto">
|
||||||
|
<ThreadSelector onClose={() => setIsThreadSelectorOpen(false)} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="overflow-y-auto overflow-x-hidden space-y-4">
|
<div className="overflow-y-auto overflow-x-hidden space-y-4">
|
||||||
{/* previous messages */}
|
{/* previous messages */}
|
||||||
{chatMessageHistory.map((message, i) =>
|
{currentThread !== null && currentThread.messages.map((message, i) =>
|
||||||
<ChatBubble key={i} chatMessage={message} />
|
<ChatBubble key={i} chatMessage={message} />
|
||||||
)}
|
)}
|
||||||
{/* message stream */}
|
{/* message stream */}
|
||||||
|
|
@ -276,21 +259,65 @@ const Sidebar = () => {
|
||||||
<div className="shrink-0 py-4">
|
<div className="shrink-0 py-4">
|
||||||
{/* selection */}
|
{/* selection */}
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
{/* selected files */}
|
|
||||||
<FilesSelector files={files} setFiles={setFiles} />
|
<div className="relative">
|
||||||
{/* selected code */}
|
<div className="input">
|
||||||
{!selection?.selectionStr ? null
|
{/* selection */}
|
||||||
: (
|
{(files.length || selection?.selectionStr) && <div className="p-2 pb-0 space-y-2">
|
||||||
<div className="relative">
|
{/* selected files */}
|
||||||
<button
|
<SelectedFiles files={files} setFiles={setFiles} />
|
||||||
onClick={clearSelection}
|
{/* selected code */}
|
||||||
className="absolute top-2 right-2 text-white hover:text-gray-300 z-10"
|
{!!selection?.selectionStr && (
|
||||||
>
|
<BlockCode className="rounded bg-vscode-sidebar-bg" text={selection.selectionStr} toolbar={(
|
||||||
X
|
<button
|
||||||
</button>
|
onClick={clearSelection}
|
||||||
<BlockCode text={selection.selectionStr} disableApplyButton={true} />
|
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||||
</div>
|
>
|
||||||
)}
|
Remove
|
||||||
|
</button>
|
||||||
|
)} />
|
||||||
|
)}
|
||||||
|
</div>}
|
||||||
|
<form
|
||||||
|
ref={formRef}
|
||||||
|
className="flex flex-row items-center rounded-md p-2"
|
||||||
|
onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) onSubmit(e) }}
|
||||||
|
|
||||||
|
onSubmit={(e) => {
|
||||||
|
console.log('submit!')
|
||||||
|
e.preventDefault();
|
||||||
|
onSubmit(e)
|
||||||
|
}}>
|
||||||
|
{/* input */}
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
onChange={(e) => { setInstructions(e.target.value) }}
|
||||||
|
className="w-full p-2 leading-tight resize-none max-h-[50vh] overflow-hidden bg-transparent border-none !outline-none"
|
||||||
|
placeholder="Ctrl+L to select"
|
||||||
|
rows={1}
|
||||||
|
onInput={e => { e.currentTarget.style.height = 'auto'; e.currentTarget.style.height = e.currentTarget.scrollHeight + 'px' }} // Adjust height dynamically
|
||||||
|
/>
|
||||||
|
{/* submit button */}
|
||||||
|
{isLoading ?
|
||||||
|
<button
|
||||||
|
onClick={onStop}
|
||||||
|
className="btn btn-primary rounded-r-lg max-h-10 p-2"
|
||||||
|
type='button'
|
||||||
|
>Stop</button>
|
||||||
|
: <button
|
||||||
|
className="btn btn-primary font-bold size-8 flex justify-center items-center rounded-full p-2 max-h-10"
|
||||||
|
disabled={!instructions}
|
||||||
|
type='submit'
|
||||||
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<line x1="12" y1="19" x2="12" y2="5"></line>
|
||||||
|
<polyline points="5 12 12 5 19 12"></polyline>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* error message */}
|
{/* error message */}
|
||||||
{requestFailed && (
|
{requestFailed && (
|
||||||
|
|
|
||||||
48
extensions/void/src/sidebar/components/SelectedFiles.tsx
Normal file
48
extensions/void/src/sidebar/components/SelectedFiles.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import React from "react"
|
||||||
|
import * as vscode from "vscode"
|
||||||
|
|
||||||
|
const getBasename = (pathStr: string) => {
|
||||||
|
// "unixify" path
|
||||||
|
pathStr = pathStr.replace(/[/\\]+/g, "/") // replace any / or \ or \\ with /
|
||||||
|
const parts = pathStr.split("/") // split on /
|
||||||
|
return parts[parts.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SelectedFiles = ({ files, setFiles, }: { files: vscode.Uri[], setFiles: null | ((files: vscode.Uri[]) => void) }) => {
|
||||||
|
return (
|
||||||
|
files.length !== 0 && (
|
||||||
|
<div className="flex flex-wrap -mx-1 -mb-1">
|
||||||
|
{files.map((filename, i) => (
|
||||||
|
<button
|
||||||
|
key={filename.path}
|
||||||
|
disabled={!setFiles}
|
||||||
|
className={`btn btn-secondary btn-sm border border-vscode-input-border rounded flex items-center space-x-2 mx-1 mb-1 disabled:cursor-default`}
|
||||||
|
type="button"
|
||||||
|
onClick={() => setFiles?.([...files.slice(0, i), ...files.slice(i + 1, Infinity)])}
|
||||||
|
>
|
||||||
|
<span>{getBasename(filename.fsPath)}</span>
|
||||||
|
|
||||||
|
{/* X button */}
|
||||||
|
{!!setFiles && <span className="">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
className="size-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M6 18 18 6M6 6l12 12"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10,6 +10,11 @@ const awaiting: { [c in Command]: ((res: any) => void)[] } = {
|
||||||
"files": [],
|
"files": [],
|
||||||
"apiConfig": [],
|
"apiConfig": [],
|
||||||
"getApiConfig": [],
|
"getApiConfig": [],
|
||||||
|
"startNewThread": [],
|
||||||
|
"getAllThreads": [],
|
||||||
|
"allThreads": [],
|
||||||
|
"persistThread": [],
|
||||||
|
"toggleThreadSelector": []
|
||||||
}
|
}
|
||||||
|
|
||||||
// use this function to await responses
|
// use this function to await responses
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,20 @@
|
||||||
import * as React from 'react'
|
import * as React from "react"
|
||||||
import * as ReactDOM from 'react-dom/client'
|
import * as ReactDOM from "react-dom/client"
|
||||||
import Sidebar from './Sidebar'
|
import Sidebar from "./Sidebar"
|
||||||
|
import { ThreadsProvider } from "./threadsContext"
|
||||||
|
|
||||||
// mount the sidebar on the id="root" element
|
// mount the sidebar on the id="root" element
|
||||||
if (typeof document === 'undefined') {
|
if (typeof document === "undefined") {
|
||||||
console.log('index.tsx error: document was undefined')
|
console.log("index.tsx error: document was undefined")
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootElement = document.getElementById('root')!
|
const rootElement = document.getElementById("root")!
|
||||||
console.log('root Element', rootElement)
|
console.log("Void root Element:", rootElement)
|
||||||
|
|
||||||
|
const extension = (
|
||||||
|
<ThreadsProvider>
|
||||||
|
<Sidebar />
|
||||||
|
</ThreadsProvider>
|
||||||
|
)
|
||||||
const root = ReactDOM.createRoot(rootElement)
|
const root = ReactDOM.createRoot(rootElement)
|
||||||
root.render(<Sidebar />)
|
root.render(extension)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
80
extensions/void/src/sidebar/markdown/BlockCode.tsx
Normal file
80
extensions/void/src/sidebar/markdown/BlockCode.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import React, { ReactNode, useCallback, useEffect, useState } from "react"
|
||||||
|
import { getVSCodeAPI } from "../getVscodeApi"
|
||||||
|
|
||||||
|
enum CopyButtonState {
|
||||||
|
Copy = "Copy",
|
||||||
|
Copied = "Copied!",
|
||||||
|
Error = "Could not copy",
|
||||||
|
}
|
||||||
|
|
||||||
|
const COPY_FEEDBACK_TIMEOUT = 1000
|
||||||
|
|
||||||
|
// code block with toolbar (Apply, Copy, etc) at top
|
||||||
|
const BlockCode = ({
|
||||||
|
text,
|
||||||
|
toolbar,
|
||||||
|
hideToolbar = false,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
text: string
|
||||||
|
toolbar?: ReactNode
|
||||||
|
hideToolbar?: boolean
|
||||||
|
className?: string
|
||||||
|
}) => {
|
||||||
|
const [copyButtonState, setCopyButtonState] = useState(CopyButtonState.Copy)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (copyButtonState !== CopyButtonState.Copy) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setCopyButtonState(CopyButtonState.Copy)
|
||||||
|
}, COPY_FEEDBACK_TIMEOUT)
|
||||||
|
}
|
||||||
|
}, [copyButtonState])
|
||||||
|
|
||||||
|
const onCopy = useCallback(() => {
|
||||||
|
navigator.clipboard.writeText(text).then(
|
||||||
|
() => {
|
||||||
|
setCopyButtonState(CopyButtonState.Copied)
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
setCopyButtonState(CopyButtonState.Error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}, [text])
|
||||||
|
|
||||||
|
const defaultToolbar = (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||||
|
onClick={onCopy}
|
||||||
|
>
|
||||||
|
{copyButtonState}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||||
|
onClick={async () => {
|
||||||
|
getVSCodeAPI().postMessage({ type: "applyCode", code: text })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Apply
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative group">
|
||||||
|
{!hideToolbar && (
|
||||||
|
<div className="absolute top-0 right-0 invisible group-hover:visible">
|
||||||
|
<div className="flex space-x-2 p-2">{toolbar || defaultToolbar}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={`overflow-x-auto rounded-sm text-vscode-editor-fg bg-vscode-editor-bg ${!hideToolbar ? "rounded-tl-none" : ""} ${className}`}
|
||||||
|
>
|
||||||
|
<pre className="p-2">{text}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BlockCode
|
||||||
168
extensions/void/src/sidebar/markdown/MarkdownRender.tsx
Normal file
168
extensions/void/src/sidebar/markdown/MarkdownRender.tsx
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
import React, { JSX } from "react"
|
||||||
|
import { marked, MarkedToken, Token, TokensList } from "marked"
|
||||||
|
import BlockCode from "./BlockCode"
|
||||||
|
|
||||||
|
const RenderToken = ({ token, nested = false }: { token: Token | string, nested?: boolean }): JSX.Element => {
|
||||||
|
|
||||||
|
// deal with built-in tokens first (assume marked token)
|
||||||
|
const t = token as MarkedToken
|
||||||
|
|
||||||
|
if (t.type === "space") {
|
||||||
|
return <span>{t.raw}</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "code") {
|
||||||
|
return <BlockCode text={t.text} />
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "heading") {
|
||||||
|
const HeadingTag = `h${t.depth}` as keyof JSX.IntrinsicElements
|
||||||
|
return <HeadingTag>{t.text}</HeadingTag>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "table") {
|
||||||
|
return (
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{t.header.map((cell: any, index: number) => (
|
||||||
|
<th key={index} style={{ textAlign: t.align[index] || "left" }}>
|
||||||
|
{cell.raw}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{t.rows.map((row: any[], rowIndex: number) => (
|
||||||
|
<tr key={rowIndex}>
|
||||||
|
{row.map((cell: any, cellIndex: number) => (
|
||||||
|
<td
|
||||||
|
key={cellIndex}
|
||||||
|
style={{ textAlign: t.align[cellIndex] || "left" }}
|
||||||
|
>
|
||||||
|
{cell.raw}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "hr") {
|
||||||
|
return <hr />
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "blockquote") {
|
||||||
|
return <blockquote>{t.text}</blockquote>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "list") {
|
||||||
|
const ListTag = t.ordered ? "ol" : "ul"
|
||||||
|
return (
|
||||||
|
<ListTag
|
||||||
|
start={t.start ? t.start : undefined}
|
||||||
|
className={`list-inside ${t.ordered ? "list-decimal" : "list-disc"}`}
|
||||||
|
>
|
||||||
|
{t.items.map((item, index) => (
|
||||||
|
<li key={index}>
|
||||||
|
{item.task && (
|
||||||
|
<input type="checkbox" checked={item.checked} readOnly />
|
||||||
|
)}
|
||||||
|
<MarkdownRender string={item.text} nested={true} />
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ListTag>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "paragraph") {
|
||||||
|
const contents = <>
|
||||||
|
{t.tokens.map((token, index) => (
|
||||||
|
<RenderToken key={index} token={token} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
if (nested)
|
||||||
|
return contents
|
||||||
|
return <p>{contents}</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't actually render <html> tags, just render strings of them
|
||||||
|
if (t.type === "html") {
|
||||||
|
return (
|
||||||
|
<pre>
|
||||||
|
{`<html>`}
|
||||||
|
{t.raw}
|
||||||
|
{`</html>`}
|
||||||
|
</pre>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "text" || t.type === "escape") {
|
||||||
|
return <span>{t.raw}</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "def") {
|
||||||
|
return <></> // Definitions are typically not rendered
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "link") {
|
||||||
|
return (
|
||||||
|
<a href={t.href} title={t.title ?? undefined}>
|
||||||
|
{t.text}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "image") {
|
||||||
|
return <img src={t.href} alt={t.text} title={t.title ?? undefined} />
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "strong") {
|
||||||
|
return <strong>{t.text}</strong>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "em") {
|
||||||
|
return <em>{t.text}</em>
|
||||||
|
}
|
||||||
|
|
||||||
|
// inline code
|
||||||
|
if (t.type === "codespan") {
|
||||||
|
return (
|
||||||
|
<code className="text-vscode-editor-fg bg-vscode-editor-bg px-1 rounded-sm font-mono">
|
||||||
|
{t.text}
|
||||||
|
</code>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.type === "br") {
|
||||||
|
return <br />
|
||||||
|
}
|
||||||
|
|
||||||
|
// strikethrough
|
||||||
|
if (t.type === "del") {
|
||||||
|
return <del>{t.text}</del>
|
||||||
|
}
|
||||||
|
|
||||||
|
// default
|
||||||
|
return (
|
||||||
|
<div className="bg-orange-50 rounded-sm overflow-hidden">
|
||||||
|
<span className="text-xs text-orange-500">Unknown type:</span>
|
||||||
|
{t.raw}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const MarkdownRender = ({ string, nested = false }: { string: string, nested?: boolean }) => {
|
||||||
|
const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{tokens.map((token, index) => (
|
||||||
|
<RenderToken key={index} token={token} nested={nested} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MarkdownRender
|
||||||
|
|
@ -3,33 +3,37 @@
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-size: var(--vscode-font-size);
|
font-size: var(--vscode-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
@apply cursor-pointer transition-colors;
|
@apply cursor-pointer transition-colors;
|
||||||
|
|
||||||
&.btn-primary {
|
&.btn-primary {
|
||||||
@apply bg-vscode-button-bg text-vscode-button-fg;
|
@apply bg-vscode-button-bg text-vscode-button-fg;
|
||||||
|
|
||||||
&:not(:disabled) {
|
&:not(:disabled) {
|
||||||
@apply hover:bg-vscode-button-hoverBg;
|
@apply hover:bg-vscode-button-hoverBg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-secondary {
|
&.btn-sm {
|
||||||
@apply bg-vscode-button-secondary-bg text-vscode-button-secondary-fg;
|
@apply px-3 py-1 text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
&:not(:disabled) {
|
&.btn-secondary {
|
||||||
@apply hover:bg-vscode-button-secondary-hoverBg;
|
@apply bg-vscode-button-secondary-bg text-vscode-button-secondary-fg;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
&:not(:disabled) {
|
||||||
@apply opacity-75 cursor-not-allowed;
|
@apply hover:bg-vscode-button-secondary-hoverBg;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
@apply opacity-75 cursor-not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
@apply bg-vscode-input-bg text-vscode-input-fg border-vscode-input-border;
|
@apply bg-vscode-input-bg text-vscode-input-fg border-vscode-input-border;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
100
extensions/void/src/sidebar/threadsContext.tsx
Normal file
100
extensions/void/src/sidebar/threadsContext.tsx
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
import React, { ReactNode, createContext, useCallback, useContext, useEffect, useRef, useState, } from "react"
|
||||||
|
import { ChatMessage, ChatThreads } from "../shared_types"
|
||||||
|
import { awaitVSCodeResponse, getVSCodeAPI } from "./getVscodeApi"
|
||||||
|
|
||||||
|
|
||||||
|
type ThreadsContextValue = {
|
||||||
|
readonly allThreads: ChatThreads | null,
|
||||||
|
readonly currentThread: ChatThreads[string] | null;
|
||||||
|
addMessageToHistory: (message: ChatMessage) => void;
|
||||||
|
switchToThread: (threadId: string) => void;
|
||||||
|
startNewThread: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThreadsContext = createContext<ThreadsContextValue>(undefined as unknown as ThreadsContextValue)
|
||||||
|
|
||||||
|
const createNewThread = () => ({
|
||||||
|
id: new Date().getTime().toString(),
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
messages: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// const [stateRef, setState] = useInstantState(initVal)
|
||||||
|
// setState instantly changes the value of stateRef instead of having to wait until the next render
|
||||||
|
const useInstantState = <T,>(initVal: T) => {
|
||||||
|
const stateRef = useRef<T>(initVal)
|
||||||
|
const [_, setS] = useState<T>(initVal)
|
||||||
|
const setState = useCallback((newVal: T) => {
|
||||||
|
setS(newVal);
|
||||||
|
stateRef.current = newVal;
|
||||||
|
}, [])
|
||||||
|
return [stateRef as React.RefObject<T>, setState] as const // make s.current readonly - setState handles all changes
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function ThreadsProvider({ children }: { children: ReactNode }) {
|
||||||
|
const [allThreads, setAllThreads] = useInstantState<ChatThreads>({})
|
||||||
|
const [currentThreadId, setCurrentThreadId] = useInstantState<string | null>(null)
|
||||||
|
|
||||||
|
// this loads allThreads in on mount
|
||||||
|
useEffect(() => {
|
||||||
|
getVSCodeAPI().postMessage({ type: "getAllThreads" })
|
||||||
|
awaitVSCodeResponse('allThreads')
|
||||||
|
.then(response => {
|
||||||
|
setAllThreads(response.threads)
|
||||||
|
})
|
||||||
|
}, [setAllThreads])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThreadsContext.Provider
|
||||||
|
value={{
|
||||||
|
allThreads: allThreads.current,
|
||||||
|
currentThread: currentThreadId.current === null || allThreads.current === null ? null : allThreads.current[currentThreadId.current],
|
||||||
|
addMessageToHistory: (message: ChatMessage) => {
|
||||||
|
let currentThread: ChatThreads[string]
|
||||||
|
if (!(currentThreadId.current === null || allThreads.current === null)) {
|
||||||
|
currentThread = allThreads.current[currentThreadId.current]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentThread = createNewThread()
|
||||||
|
setCurrentThreadId(currentThread.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
setAllThreads({
|
||||||
|
...allThreads.current,
|
||||||
|
[currentThread.id]: {
|
||||||
|
...currentThread,
|
||||||
|
messages: [...currentThread.messages, message],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
getVSCodeAPI().postMessage({ type: "persistThread", thread: currentThread })
|
||||||
|
},
|
||||||
|
switchToThread: (threadId: string) => {
|
||||||
|
setCurrentThreadId(threadId);
|
||||||
|
},
|
||||||
|
startNewThread: () => {
|
||||||
|
const newThread = createNewThread()
|
||||||
|
setAllThreads({
|
||||||
|
...allThreads.current,
|
||||||
|
[newThread.id]: newThread
|
||||||
|
})
|
||||||
|
setCurrentThreadId(newThread.id)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ThreadsContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useThreads(): ThreadsContextValue {
|
||||||
|
const context = useContext<ThreadsContextValue>(ThreadsContext)
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error("useThreads must be used within a ThreadsProvider")
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -7,17 +7,19 @@ module.exports = {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
vscode: {
|
vscode: {
|
||||||
'editor-bg': "var(--vscode-editor-background)",
|
"sidebar-bg": "var(--vscode-sideBar-background)",
|
||||||
'editor-fg': "var(--vscode-editor-foreground)",
|
"editor-bg": "var(--vscode-editor-background)",
|
||||||
'input-bg': "var(--vscode-input-background)",
|
"editor-fg": "var(--vscode-editor-foreground)",
|
||||||
'input-fg': "var(--vscode-input-foreground)",
|
"input-bg": "var(--vscode-input-background)",
|
||||||
'input-border': "var(--vscode-input-border)",
|
"input-fg": "var(--vscode-input-foreground)",
|
||||||
'button-fg': "var(--vscode-button-foreground)",
|
"input-border": "var(--vscode-input-border)",
|
||||||
'button-bg': "var(--vscode-button-background)",
|
"button-fg": "var(--vscode-button-foreground)",
|
||||||
'button-hoverBg': "var(--vscode-button-hoverBackground)",
|
"button-bg": "var(--vscode-button-background)",
|
||||||
'button-secondary-fg': "var(--vscode-button-secondaryForeground)",
|
"button-hoverBg": "var(--vscode-button-hoverBackground)",
|
||||||
'button-secondary-bg': "var(--vscode-button-secondaryBackground)",
|
"button-secondary-fg": "var(--vscode-button-secondaryForeground)",
|
||||||
'button-secondary-hoverBg': "var(--vscode-button-secondaryHoverBackground)",
|
"button-secondary-bg": "var(--vscode-button-secondaryBackground)",
|
||||||
|
"button-secondary-hoverBg":
|
||||||
|
"var(--vscode-button-secondaryHoverBackground)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue