mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
Merge remote-tracking branch 'origin/model-selection' into model-seln-2
This commit is contained in:
commit
901579a445
59 changed files with 1139 additions and 1174 deletions
|
|
@ -70,7 +70,7 @@ If you ran `npm run watch`, the build is done when you see something like this:
|
|||
|
||||
<!-- 3. Press <kbd>Ctrl+Shift+B</kbd> to start the build process. -->
|
||||
|
||||
4. In a new terminal, run `./scripts/code.sh` (Mac/Linux) or `./scripts/code.bat` (Windows). This should open up the built IDE!
|
||||
4. In a new terminal, run `./scripts/code.sh` (Mac/Linux) or `./scripts/code.bat` (Windows). This should open up the built IDE.
|
||||
You can always press <kbd>Ctrl+Shift+P</kbd> and run "Reload Window" inside the new window to see changes without re-building.
|
||||
|
||||
Now that you're set up, feel free to check out our [Issues](https://github.com/voideditor/void/issues) page.
|
||||
|
|
@ -84,10 +84,20 @@ Now that you're set up, feel free to check out our [Issues](https://github.com/v
|
|||
|
||||
## Bundling
|
||||
|
||||
To bundle the IDE into an executable, run `npm run gulp vscode-darwin-arm64`.
|
||||
We don't usually recommend bundling - instead, usually you should just build (above). To bundle Void into an executable app, run one of the following commands. This will create a folder named `VSCode-darwin-arm64` (or similar) in the repo's parent's directory. Be patient - compiling can take ~25 minutes.
|
||||
|
||||
Here are the full options: `vscode-{win32-ia32 | win32-x64 | darwin-x64 | darwin-arm64 | linux-ia32 | linux-x64 | linux-arm}(-min)`
|
||||
### Mac
|
||||
- `npm run gulp vscode-darwin-arm64` - most common (Apple Silicon)
|
||||
- `npm run gulp vscode-darwin-x64` (Intel)
|
||||
|
||||
### Windows
|
||||
- `npm run gulp vscode-win32-x64` - most common
|
||||
- `npm run gulp vscode-win32-ia32`
|
||||
|
||||
### Linux
|
||||
- `npm run gulp vscode-linux-x64` - most common
|
||||
- `npm run gulp vscode-linux-arm`
|
||||
- `npm run gulp vscode-linux-ia32`
|
||||
|
||||
## Roadmap
|
||||
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ function createServerHost(
|
|||
try {
|
||||
fs.createDirectory(pathMapper.toResource(path));
|
||||
} catch (error) {
|
||||
logger.logNormal('Error fs.createDirectory', { path, error: error + '' });
|
||||
logger.logNormal('Error fs.createDirectory', { path, error });
|
||||
}
|
||||
},
|
||||
getExecutingFilePath(): string {
|
||||
|
|
@ -323,7 +323,7 @@ function createServerHost(
|
|||
try {
|
||||
fs.delete(pathMapper.toResource(path));
|
||||
} catch (error) {
|
||||
logger.logNormal('Error fs.deleteFile', { path, error: error + '' });
|
||||
logger.logNormal('Error fs.deleteFile', { path, error });
|
||||
}
|
||||
},
|
||||
createHash: generateDjb2Hash,
|
||||
|
|
|
|||
90
package-lock.json
generated
90
package-lock.json
generated
|
|
@ -10,6 +10,8 @@
|
|||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.32.1",
|
||||
"@google/generative-ai": "^0.21.0",
|
||||
"@microsoft/1ds-core-js": "^3.2.13",
|
||||
"@microsoft/1ds-post-js": "^3.2.13",
|
||||
"@parcel/watcher": "2.1.0",
|
||||
|
|
@ -37,6 +39,7 @@
|
|||
"@xterm/addon-webgl": "^0.19.0-beta.64",
|
||||
"@xterm/headless": "^5.6.0-beta.64",
|
||||
"@xterm/xterm": "^5.6.0-beta.64",
|
||||
"diff": "^7.0.0",
|
||||
"groq-sdk": "^0.9.0",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
|
|
@ -48,8 +51,13 @@
|
|||
"native-keymap": "^3.3.5",
|
||||
"native-watchdog": "^1.4.1",
|
||||
"node-pty": "1.1.0-beta21",
|
||||
"ollama": "^0.5.11",
|
||||
"open": "^8.4.2",
|
||||
"openai": "^4.76.1",
|
||||
"posthog-node": "^4.3.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"tas-client-umd": "0.2.0",
|
||||
"v8-inspect-profiler": "^0.1.1",
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
|
|
@ -59,13 +67,12 @@
|
|||
"yazl": "^2.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "^0.32.1",
|
||||
"@google/generative-ai": "^0.21.0",
|
||||
"@playwright/test": "^1.46.1",
|
||||
"@swc/core": "1.3.62",
|
||||
"@types/cookie": "^0.3.3",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/diff": "^6.0.0",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/gulp-svgmin": "^1.2.1",
|
||||
"@types/http-proxy-agent": "^2.0.1",
|
||||
"@types/kerberos": "^1.1.2",
|
||||
|
|
@ -106,7 +113,6 @@
|
|||
"cssnano": "^6.0.3",
|
||||
"debounce": "^1.0.0",
|
||||
"deemon": "^1.8.0",
|
||||
"diff": "^7.0.0",
|
||||
"electron": "30.5.1",
|
||||
"eslint": "8.36.0",
|
||||
"eslint-plugin-header": "3.1.1",
|
||||
|
|
@ -150,8 +156,6 @@
|
|||
"mocha-junit-reporter": "^2.2.1",
|
||||
"mocha-multi-reporters": "^1.5.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"ollama": "^0.5.9",
|
||||
"openai": "^4.71.1",
|
||||
"opn": "^6.0.0",
|
||||
"original-fs": "^1.2.0",
|
||||
"os-browserify": "^0.3.0",
|
||||
|
|
@ -161,11 +165,8 @@
|
|||
"postcss-nesting": "^12.0.2",
|
||||
"pump": "^1.0.1",
|
||||
"rcedit": "^1.1.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"rimraf": "^2.7.1",
|
||||
"scope-tailwind": "^1.0.1",
|
||||
"scope-tailwind": "^1.0.5",
|
||||
"sinon": "^12.0.1",
|
||||
"sinon-test": "^3.1.3",
|
||||
"source-map": "0.6.1",
|
||||
|
|
@ -219,7 +220,6 @@
|
|||
"version": "0.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.32.1.tgz",
|
||||
"integrity": "sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
|
|
@ -235,7 +235,6 @@
|
|||
"version": "18.19.64",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.64.tgz",
|
||||
"integrity": "sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
|
|
@ -1003,7 +1002,6 @@
|
|||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
|
|
@ -1684,7 +1682,6 @@
|
|||
"version": "0.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz",
|
||||
"integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
|
@ -3021,6 +3018,17 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
||||
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
|
|
@ -3060,7 +3068,6 @@
|
|||
"version": "2.3.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
|
||||
"integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2"
|
||||
|
|
@ -3247,7 +3254,6 @@
|
|||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
|
||||
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/vinyl": {
|
||||
|
|
@ -5850,7 +5856,6 @@
|
|||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
|
||||
"integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -5861,7 +5866,6 @@
|
|||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
|
||||
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -5872,7 +5876,6 @@
|
|||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
|
||||
"integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -6362,7 +6365,6 @@
|
|||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
|
||||
"integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -7248,7 +7250,6 @@
|
|||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
|
||||
"integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
|
|
@ -8497,7 +8498,6 @@
|
|||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
|
||||
"integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"format": "^0.2.0"
|
||||
|
|
@ -9021,7 +9021,6 @@
|
|||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
|
||||
"integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.x"
|
||||
}
|
||||
|
|
@ -12029,7 +12028,6 @@
|
|||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
|
||||
"integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
|
|
@ -12040,7 +12038,6 @@
|
|||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
|
||||
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^2.0.0",
|
||||
|
|
@ -12067,7 +12064,6 @@
|
|||
"version": "10.7.3",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
|
|
@ -12077,7 +12073,6 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz",
|
||||
"integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==",
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/homedir-polyfill": {
|
||||
|
|
@ -12723,7 +12718,6 @@
|
|||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
|
||||
"integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -12734,7 +12728,6 @@
|
|||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
|
||||
"integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-alphabetical": "^1.0.0",
|
||||
|
|
@ -12946,7 +12939,6 @@
|
|||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
|
||||
"integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -13073,7 +13065,6 @@
|
|||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
|
||||
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -13611,8 +13602,7 @@
|
|||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
|
|
@ -14292,7 +14282,6 @@
|
|||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
|
|
@ -14314,7 +14303,6 @@
|
|||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
|
||||
"integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fault": "^1.0.0",
|
||||
|
|
@ -15984,10 +15972,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ollama": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ollama/-/ollama-0.5.9.tgz",
|
||||
"integrity": "sha512-F/KZuDRC+ZsVCuMvcOYuQ6zj42/idzCkkuknGyyGVmNStMZ/sU3jQpvhnl4SyC0+zBzLiKNZJnJeuPFuieWZvQ==",
|
||||
"dev": true,
|
||||
"version": "0.5.11",
|
||||
"resolved": "https://registry.npmjs.org/ollama/-/ollama-0.5.11.tgz",
|
||||
"integrity": "sha512-lDAKcpmBU3VAOGF05NcQipHNKTdpKfAHpZ7bjCsElkUkmX7SNZImi6lwIxz/l1zQtLq0S3wuLneRuiXxX2KIew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-fetch": "^3.6.20"
|
||||
|
|
@ -16060,10 +16047,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/openai": {
|
||||
"version": "4.71.1",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-4.71.1.tgz",
|
||||
"integrity": "sha512-C6JNMaQ1eijM0lrjiRUL3MgThVP5RdwNAghpbJFdW0t11LzmyqON8Eh8MuUuEZ+CeD6bgYl2Fkn2BoptVxv9Ug==",
|
||||
"dev": true,
|
||||
"version": "4.76.1",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-4.76.1.tgz",
|
||||
"integrity": "sha512-ci63/WFEMd6QjjEVeH0pV7hnFS6CCqhgJydSti4Aak/8uo2SpgzKjteUDaY+OkwziVj11mi6j+0mRUIiGKUzWw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
|
|
@ -16090,7 +16076,6 @@
|
|||
"version": "18.19.64",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.64.tgz",
|
||||
"integrity": "sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
|
|
@ -16400,7 +16385,6 @@
|
|||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
|
||||
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"character-entities": "^1.0.0",
|
||||
|
|
@ -17736,7 +17720,6 @@
|
|||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
|
||||
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
|
|
@ -17770,7 +17753,6 @@
|
|||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
|
||||
"integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
|
|
@ -17961,7 +17943,6 @@
|
|||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
|
|
@ -17974,7 +17955,6 @@
|
|||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
|
|
@ -17988,7 +17968,6 @@
|
|||
"version": "15.6.1",
|
||||
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz",
|
||||
"integrity": "sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
|
|
@ -18206,7 +18185,6 @@
|
|||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz",
|
||||
"integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hastscript": "^6.0.0",
|
||||
|
|
@ -18222,7 +18200,6 @@
|
|||
"version": "1.27.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
|
||||
"integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
|
|
@ -18232,7 +18209,6 @@
|
|||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regex-not": {
|
||||
|
|
@ -18889,7 +18865,6 @@
|
|||
"version": "0.23.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
|
|
@ -18949,9 +18924,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/scope-tailwind": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/scope-tailwind/-/scope-tailwind-1.0.1.tgz",
|
||||
"integrity": "sha512-u65hZidTP5LhJL2jufMQwi9ulqdJ6NGx3gYKDwuTYQJz/oaYpOs76cLHCp4ul79KVr7lPWRHTlSqLfs55wrqyA==",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/scope-tailwind/-/scope-tailwind-1.0.5.tgz",
|
||||
"integrity": "sha512-h7WhPy9LocCjtG0r2ijLJmVa0huJlk8wrlCSEOV08qowsNgFScVMqY8ocIbIfnZvgcOyZlUWtCjO+sf6Q0T9nA==",
|
||||
"dev": true,
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
|
|
@ -19743,7 +19718,6 @@
|
|||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
|
||||
"integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -22642,7 +22616,6 @@
|
|||
"version": "3.6.20",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
|
||||
"integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
|
|
@ -22876,7 +22849,6 @@
|
|||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
|
|
|
|||
21
package.json
21
package.json
|
|
@ -9,7 +9,7 @@
|
|||
"main": "./out/main",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "cd ./src/vs/workbench/contrib/void/browser/react/ && node build.js && cd ../../../../../../../",
|
||||
"buildreact": "cd ./src/vs/workbench/contrib/void/browser/react/ && node build.js && cd ../../../../../../../",
|
||||
"test": "echo Please run any of the test scripts from the scripts folder.",
|
||||
"test-browser": "npx playwright install && node test/unit/browser/index.js",
|
||||
"test-browser-amd": "npx playwright install && node test/unit/browser/index.amd.js",
|
||||
|
|
@ -73,6 +73,8 @@
|
|||
"update-build-ts-version": "npm install typescript@next && tsc -p ./build/tsconfig.build.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.32.1",
|
||||
"@google/generative-ai": "^0.21.0",
|
||||
"@microsoft/1ds-core-js": "^3.2.13",
|
||||
"@microsoft/1ds-post-js": "^3.2.13",
|
||||
"@parcel/watcher": "2.1.0",
|
||||
|
|
@ -100,6 +102,7 @@
|
|||
"@xterm/addon-webgl": "^0.19.0-beta.64",
|
||||
"@xterm/headless": "^5.6.0-beta.64",
|
||||
"@xterm/xterm": "^5.6.0-beta.64",
|
||||
"diff": "^7.0.0",
|
||||
"groq-sdk": "^0.9.0",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
|
|
@ -111,8 +114,13 @@
|
|||
"native-keymap": "^3.3.5",
|
||||
"native-watchdog": "^1.4.1",
|
||||
"node-pty": "1.1.0-beta21",
|
||||
"ollama": "^0.5.11",
|
||||
"open": "^8.4.2",
|
||||
"openai": "^4.76.1",
|
||||
"posthog-node": "^4.3.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"tas-client-umd": "0.2.0",
|
||||
"v8-inspect-profiler": "^0.1.1",
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
|
|
@ -122,13 +130,12 @@
|
|||
"yazl": "^2.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "^0.32.1",
|
||||
"@google/generative-ai": "^0.21.0",
|
||||
"@playwright/test": "^1.46.1",
|
||||
"@swc/core": "1.3.62",
|
||||
"@types/cookie": "^0.3.3",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/diff": "^6.0.0",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/gulp-svgmin": "^1.2.1",
|
||||
"@types/http-proxy-agent": "^2.0.1",
|
||||
"@types/kerberos": "^1.1.2",
|
||||
|
|
@ -169,7 +176,6 @@
|
|||
"cssnano": "^6.0.3",
|
||||
"debounce": "^1.0.0",
|
||||
"deemon": "^1.8.0",
|
||||
"diff": "^7.0.0",
|
||||
"electron": "30.5.1",
|
||||
"eslint": "8.36.0",
|
||||
"eslint-plugin-header": "3.1.1",
|
||||
|
|
@ -213,8 +219,6 @@
|
|||
"mocha-junit-reporter": "^2.2.1",
|
||||
"mocha-multi-reporters": "^1.5.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"ollama": "^0.5.9",
|
||||
"openai": "^4.71.1",
|
||||
"opn": "^6.0.0",
|
||||
"original-fs": "^1.2.0",
|
||||
"os-browserify": "^0.3.0",
|
||||
|
|
@ -224,11 +228,8 @@
|
|||
"postcss-nesting": "^12.0.2",
|
||||
"pump": "^1.0.1",
|
||||
"rcedit": "^1.1.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"rimraf": "^2.7.1",
|
||||
"scope-tailwind": "^1.0.1",
|
||||
"scope-tailwind": "^1.0.5",
|
||||
"sinon": "^12.0.1",
|
||||
"sinon-test": "^3.1.3",
|
||||
"source-map": "0.6.1",
|
||||
|
|
|
|||
|
|
@ -120,6 +120,8 @@ import { AuxiliaryWindowsMainService } from '../../platform/auxiliaryWindow/elec
|
|||
import { normalizeNFC } from '../../base/common/normalization.js';
|
||||
import { ICSSDevelopmentService, CSSDevelopmentService } from '../../platform/cssDev/node/cssDevService.js';
|
||||
import { ExtensionSignatureVerificationService, IExtensionSignatureVerificationService } from '../../platform/extensionManagement/node/extensionSignatureVerificationService.js';
|
||||
|
||||
import { LLMMessageChannel } from '../../platform/void/electron-main/llmMessageChannel.js';
|
||||
import { IMetricsService } from '../../platform/void/common/metricsService.js';
|
||||
import { MetricsMainService } from '../../platform/void/electron-main/metricsMainService.js';
|
||||
|
||||
|
|
@ -1240,6 +1242,12 @@ export class CodeApplication extends Disposable {
|
|||
mainProcessElectronServer.registerChannel('logger', loggerChannel);
|
||||
sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel));
|
||||
|
||||
// Void
|
||||
const metricsChannel = ProxyChannel.fromService(accessor.get(IMetricsService), disposables);
|
||||
mainProcessElectronServer.registerChannel('void-channel-metrics', metricsChannel);
|
||||
const sendLLMMessageChannel = new LLMMessageChannel(accessor.get(IMetricsService));
|
||||
mainProcessElectronServer.registerChannel('void-channel-sendLLMMessage', sendLLMMessageChannel);
|
||||
|
||||
// Extension Host Debug Broadcasting
|
||||
const electronExtensionHostDebugBroadcastChannel = new ElectronExtensionHostDebugBroadcastChannel(accessor.get(IWindowsMainService));
|
||||
mainProcessElectronServer.registerChannel('extensionhostdebugservice', electronExtensionHostDebugBroadcastChannel);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, LLMMessageServiceParams, ProxyLLMMessageParams, ProxyLLMMessageAbortParams } from '../common/llmMessageTypes.js';
|
||||
|
|
@ -10,7 +10,9 @@ import { InstantiationType, registerSingleton } from '../../instantiation/common
|
|||
import { generateUuid } from '../../../base/common/uuid.js';
|
||||
import { createDecorator } from '../../instantiation/common/instantiation.js';
|
||||
import { Event } from '../../../base/common/event.js';
|
||||
import { IDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { Disposable } from '../../../base/common/lifecycle.js';
|
||||
import { IVoidConfigStateService } from '../common/voidConfigService.js';
|
||||
import { INotificationService } from '../../notification/common/notification.js';
|
||||
|
||||
|
||||
// BROWSER IMPLEMENTATION OF SENDLLMMESSAGE
|
||||
|
|
@ -19,85 +21,101 @@ export const ISendLLMMessageService = createDecorator<ISendLLMMessageService>('s
|
|||
// defines an interface that node/ creates and browser/ uses
|
||||
export interface ISendLLMMessageService {
|
||||
readonly _serviceBrand: undefined;
|
||||
sendLLMMessage: (params: LLMMessageServiceParams) => string;
|
||||
sendLLMMessage: (params: LLMMessageServiceParams) => string | null;
|
||||
abort: (requestId: string) => void;
|
||||
}
|
||||
|
||||
|
||||
export class SendLLMMessageService implements ISendLLMMessageService {
|
||||
export class SendLLMMessageService extends Disposable implements ISendLLMMessageService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly channel: IChannel;
|
||||
private readonly channel: IChannel // LLMMessageChannel
|
||||
|
||||
private readonly _disposablesOfRequestId: Record<string, IDisposable[]> = {}
|
||||
private readonly onTextHooks: { [eventId: string]: ((params: ProxyOnTextPayload) => void) } = {}
|
||||
private readonly onFinalMessageHooks: { [eventId: string]: ((params: ProxyOnFinalMessagePayload) => void) } = {}
|
||||
private readonly onErrorHooks: { [eventId: string]: ((params: ProxyOnErrorPayload) => void) } = {}
|
||||
|
||||
constructor(
|
||||
@IMainProcessService mainProcessService: IMainProcessService // used as a renderer (only usable on client side)
|
||||
@IMainProcessService private readonly mainProcessService: IMainProcessService, // used as a renderer (only usable on client side)
|
||||
@IVoidConfigStateService private readonly voidConfigStateService: IVoidConfigStateService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
) {
|
||||
super()
|
||||
|
||||
this.channel = mainProcessService.getChannel('void-channel-sendLLMMessage')
|
||||
// const service = ProxyChannel.toService<LLMMessageChannel>(mainProcessService.getChannel('void-channel-sendLLMMessage')); // lets you call it like a service, not needed here
|
||||
// const service = ProxyChannel.toService<LLMMessageChannel>(mainProcessService.getChannel('void-channel-sendLLMMessage')); // lets you call it like a service
|
||||
this.channel = this.mainProcessService.getChannel('void-channel-sendLLMMessage')
|
||||
|
||||
// this sets up an IPC channel and takes a few ms, so we set up listeners immediately and add hooks to them instead
|
||||
const onTextEvent: Event<ProxyOnTextPayload> = this.channel.listen('onText')
|
||||
const onFinalMessageEvent: Event<ProxyOnFinalMessagePayload> = this.channel.listen('onFinalMessage')
|
||||
const onErrorEvent: Event<ProxyOnErrorPayload> = this.channel.listen('onError')
|
||||
|
||||
this._register(
|
||||
onTextEvent(e => {
|
||||
this.onTextHooks[e.requestId]?.(e)
|
||||
})
|
||||
)
|
||||
|
||||
this._register(
|
||||
onFinalMessageEvent(e => {
|
||||
this.onFinalMessageHooks[e.requestId]?.(e)
|
||||
this._onRequestIdDone(e.requestId)
|
||||
})
|
||||
)
|
||||
|
||||
this._register(
|
||||
onErrorEvent(e => {
|
||||
console.log('Error in SendLLMMessageService:', JSON.stringify(e))
|
||||
this.onErrorHooks[e.requestId]?.(e)
|
||||
this._onRequestIdDone(e.requestId)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
_addDisposable(requestId: string, disposable: IDisposable) {
|
||||
if (!this._disposablesOfRequestId[requestId]) {
|
||||
this._disposablesOfRequestId[requestId] = []
|
||||
}
|
||||
this._disposablesOfRequestId[requestId].push(disposable)
|
||||
}
|
||||
|
||||
|
||||
|
||||
sendLLMMessage(params: LLMMessageServiceParams) {
|
||||
const requestId_ = generateUuid();
|
||||
const { onText, onFinalMessage, onError, ...proxyParams } = params;
|
||||
const { featureName } = proxyParams
|
||||
|
||||
// listen for listenerName='onText' | 'onFinalMessage' | 'onError', and call the original function on it
|
||||
// end early if no provider
|
||||
const modelSelection = this.voidConfigStateService.state.modelSelectionOfFeature[featureName]
|
||||
if (modelSelection === null) {
|
||||
this.notificationService.warn('Please add a Provider in Settings!')
|
||||
onError({ message: 'Please add a Provider in Settings!', fullError: null })
|
||||
return null
|
||||
}
|
||||
const { providerName, modelName } = modelSelection
|
||||
|
||||
const onTextEvent: Event<ProxyOnTextPayload> = this.channel.listen('onText')
|
||||
this._addDisposable(requestId_,
|
||||
onTextEvent(e => {
|
||||
if (requestId_ !== e.requestId) return;
|
||||
onText(e)
|
||||
})
|
||||
)
|
||||
// add state for request id
|
||||
const requestId_ = generateUuid();
|
||||
this.onTextHooks[requestId_] = onText
|
||||
this.onFinalMessageHooks[requestId_] = onFinalMessage
|
||||
this.onErrorHooks[requestId_] = onError
|
||||
|
||||
const onFinalMessageEvent: Event<ProxyOnFinalMessagePayload> = this.channel.listen('onFinalMessage')
|
||||
this._addDisposable(requestId_,
|
||||
onFinalMessageEvent(e => {
|
||||
if (requestId_ !== e.requestId) return;
|
||||
onFinalMessage(e)
|
||||
this._dispose(requestId_)
|
||||
})
|
||||
)
|
||||
const { settingsOfProvider } = this.voidConfigStateService.state
|
||||
|
||||
const onErrorEvent: Event<ProxyOnErrorPayload> = this.channel.listen('onError')
|
||||
this._addDisposable(requestId_,
|
||||
onErrorEvent(e => {
|
||||
if (requestId_ !== e.requestId) return;
|
||||
console.log('event onError', JSON.stringify(e))
|
||||
onError(e)
|
||||
this._dispose(requestId_)
|
||||
})
|
||||
)
|
||||
|
||||
// params will be stripped of all its functions
|
||||
this.channel.call('sendLLMMessage', { ...proxyParams, requestId: requestId_ } satisfies ProxyLLMMessageParams);
|
||||
// params will be stripped of all its functions over the IPC channel
|
||||
this.channel.call('sendLLMMessage', {
|
||||
...proxyParams,
|
||||
requestId: requestId_,
|
||||
providerName,
|
||||
modelName,
|
||||
settingsOfProvider,
|
||||
} satisfies ProxyLLMMessageParams);
|
||||
|
||||
return requestId_
|
||||
}
|
||||
|
||||
private _dispose(requestId: string) {
|
||||
if (!(requestId in this._disposablesOfRequestId)) return
|
||||
for (const disposable of this._disposablesOfRequestId[requestId]) {
|
||||
disposable.dispose()
|
||||
}
|
||||
delete this._disposablesOfRequestId[requestId]
|
||||
}
|
||||
|
||||
abort(requestId: string) {
|
||||
this.channel.call('abort', { requestId } satisfies ProxyLLMMessageAbortParams);
|
||||
this._dispose(requestId)
|
||||
this._onRequestIdDone(requestId)
|
||||
}
|
||||
|
||||
_onRequestIdDone(requestId: string) {
|
||||
delete this.onTextHooks[requestId]
|
||||
delete this.onFinalMessageHooks[requestId]
|
||||
delete this.onErrorHooks[requestId]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IChannel } from '../../../base/parts/ipc/common/ipc.js';
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ProviderName, VoidProviderState } from './configTypes'
|
||||
import { IRange } from '../../../editor/common/core/range'
|
||||
import { ProviderName, SettingsOfProvider } from './voidConfigTypes'
|
||||
|
||||
|
||||
// ---------- type definitions ----------
|
||||
|
||||
export type OnText = (p: { newText: string, fullText: string }) => void
|
||||
|
||||
export type OnFinalMessage = (p: { fullText: string }) => void
|
||||
|
||||
export type OnError = (p: { error: Error | string }) => void
|
||||
export type OnError = (p: { message: string, fullError: Error | null }) => void
|
||||
|
||||
export type AbortRef = { current: (() => void) | null }
|
||||
|
||||
|
|
@ -21,42 +20,67 @@ export type LLMMessage = {
|
|||
content: string;
|
||||
}
|
||||
|
||||
export type LLMFeatureSelection = {
|
||||
featureName: 'Ctrl+K',
|
||||
range: IRange
|
||||
} | {
|
||||
featureName: 'Ctrl+L',
|
||||
} | {
|
||||
featureName: 'Autocomplete',
|
||||
range: IRange
|
||||
}
|
||||
|
||||
export type LLMMessageServiceParams = {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
|
||||
messages: LLMMessage[];
|
||||
voidConfig: VoidProviderState | null;
|
||||
|
||||
logging: {
|
||||
loggingName: string,
|
||||
};
|
||||
providerName: ProviderName;
|
||||
|
||||
}
|
||||
} & LLMFeatureSelection
|
||||
|
||||
// params to the true sendLLMMessage function
|
||||
export type SendLLMMMessageParams = {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
abortRef: AbortRef;
|
||||
|
||||
messages: LLMMessage[];
|
||||
voidConfig: VoidProviderState | null;
|
||||
|
||||
logging: {
|
||||
loggingName: string,
|
||||
};
|
||||
providerName: ProviderName;
|
||||
abortRef: AbortRef;
|
||||
modelName: string;
|
||||
settingsOfProvider: SettingsOfProvider;
|
||||
}
|
||||
|
||||
// can't send functions across a proxy, use listeners instead
|
||||
export const listenerNames = ['onText', 'onFinalMessage', 'onError'] as const
|
||||
export type ProxyLLMMessageParams = Omit<LLMMessageServiceParams, typeof listenerNames[number]> & { requestId: string }
|
||||
export type BlockedProxyParams = 'onText' | 'onFinalMessage' | 'onError' | 'abortRef'
|
||||
export type ProxyLLMMessageParams = Omit<SendLLMMMessageParams, BlockedProxyParams> & { requestId: string }
|
||||
|
||||
export type ProxyOnTextPayload = Parameters<OnText>[0] & { requestId: string }
|
||||
export type ProxyOnFinalMessagePayload = Parameters<OnFinalMessage>[0] & { requestId: string }
|
||||
export type ProxyOnErrorPayload = Parameters<OnError>[0] & { requestId: string }
|
||||
|
||||
export type ProxyLLMMessageAbortParams = { requestId: string }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export type SendLLMMessageFnTypeInternal = (params: {
|
||||
messages: LLMMessage[];
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
settingsOfProvider: SettingsOfProvider;
|
||||
providerName: ProviderName;
|
||||
modelName: string;
|
||||
|
||||
_setAborter: (aborter: () => void) => void;
|
||||
}) => void
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from '../../instantiation/common/instantiation.js';
|
||||
|
||||
export interface IMetricsService {
|
||||
|
|
|
|||
142
src/vs/platform/void/common/voidConfigService.ts
Normal file
142
src/vs/platform/void/common/voidConfigService.ts
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from '../../../base/common/event.js';
|
||||
import { Disposable } from '../../../base/common/lifecycle.js';
|
||||
import { deepClone } from '../../../base/common/objects.js';
|
||||
import { IEncryptionService } from '../../encryption/common/encryptionService.js';
|
||||
import { registerSingleton, InstantiationType } from '../../instantiation/common/extensions.js';
|
||||
import { createDecorator } from '../../instantiation/common/instantiation.js';
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js';
|
||||
import { defaultVoidProviderState, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider } from './voidConfigTypes.js';
|
||||
|
||||
|
||||
const STORAGE_KEY = 'void.voidConfigStateII'
|
||||
|
||||
type SetSettingOfProviderFn = <K extends ProviderName>(
|
||||
providerName: K,
|
||||
option: keyof SettingsOfProvider[K],
|
||||
newVal: string
|
||||
) => Promise<void>;
|
||||
|
||||
type SetModelSelectionOfFeature = <K extends FeatureName>(
|
||||
featureName: K,
|
||||
newVal: ModelSelectionOfFeature[K],
|
||||
) => Promise<void>;
|
||||
|
||||
|
||||
type VoidConfigState = {
|
||||
settingsOfProvider: SettingsOfProvider; // optionsOfProvider
|
||||
modelSelectionOfFeature: ModelSelectionOfFeature; // stateOfFeature
|
||||
}
|
||||
|
||||
export interface IVoidConfigStateService {
|
||||
readonly _serviceBrand: undefined;
|
||||
readonly state: VoidConfigState;
|
||||
onDidChangeState: Event<void>;
|
||||
onDidGetInitState: Event<void>;
|
||||
setSettingOfProvider: SetSettingOfProviderFn;
|
||||
setModelSelectionOfFeature: SetModelSelectionOfFeature;
|
||||
}
|
||||
|
||||
|
||||
const defaultState = () => {
|
||||
const d: VoidConfigState = {
|
||||
settingsOfProvider: deepClone(defaultVoidProviderState),
|
||||
modelSelectionOfFeature: { 'Ctrl+L': null, 'Ctrl+K': null, 'Autocomplete': null }
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
|
||||
export const IVoidConfigStateService = createDecorator<IVoidConfigStateService>('VoidConfigStateService');
|
||||
class VoidConfigStateService extends Disposable implements IVoidConfigStateService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes
|
||||
|
||||
private readonly _onDidGetInitState = new Emitter<void>();
|
||||
readonly onDidGetInitState: Event<void> = this._onDidGetInitState.event;
|
||||
|
||||
state: VoidConfigState;
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IEncryptionService private readonly _encryptionService: IEncryptionService,
|
||||
// could have used this, but it's clearer the way it is (+ slightly different eg StorageTarget.USER)
|
||||
// @ISecretStorageService private readonly _secretStorageService: ISecretStorageService,
|
||||
) {
|
||||
super()
|
||||
|
||||
// at the start, we haven't read the partial config yet, but we need to set state to something
|
||||
this.state = defaultState()
|
||||
|
||||
// read and update the actual state immediately
|
||||
this._readVoidConfigState().then(voidConfigState => { this._setState(voidConfigState, 'initialState') })
|
||||
|
||||
}
|
||||
|
||||
private async _readVoidConfigState(): Promise<VoidConfigState> {
|
||||
const encryptedPartialConfig = this._storageService.get(STORAGE_KEY, StorageScope.APPLICATION)
|
||||
|
||||
if (!encryptedPartialConfig)
|
||||
return defaultState()
|
||||
|
||||
const voidConfigStateStr = await this._encryptionService.decrypt(encryptedPartialConfig)
|
||||
return JSON.parse(voidConfigStateStr)
|
||||
}
|
||||
|
||||
|
||||
private async _storeVoidConfigState(voidConfigState: VoidConfigState) {
|
||||
const encryptedVoidConfigStr = await this._encryptionService.encrypt(JSON.stringify(voidConfigState))
|
||||
this._storageService.store(STORAGE_KEY, encryptedVoidConfigStr, StorageScope.APPLICATION, StorageTarget.USER);
|
||||
}
|
||||
|
||||
setSettingOfProvider: SetSettingOfProviderFn = async (providerName, option, newVal) => {
|
||||
const newState: VoidConfigState = {
|
||||
...this.state,
|
||||
settingsOfProvider: {
|
||||
...this.state.settingsOfProvider,
|
||||
[providerName]: {
|
||||
...this.state.settingsOfProvider[providerName],
|
||||
[option]: newVal,
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('NEW STATE I', newState)
|
||||
|
||||
await this._storeVoidConfigState(newState)
|
||||
this._setState(newState)
|
||||
}
|
||||
|
||||
setModelSelectionOfFeature: SetModelSelectionOfFeature = async (featureName, newVal) => {
|
||||
const newState: VoidConfigState = {
|
||||
...this.state,
|
||||
modelSelectionOfFeature: {
|
||||
...this.state.modelSelectionOfFeature,
|
||||
[featureName]: newVal
|
||||
}
|
||||
}
|
||||
console.log('NEW STATE II', newState)
|
||||
|
||||
await this._storeVoidConfigState(newState)
|
||||
this._setState(newState)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// internal function to update state, should be called every time state changes
|
||||
private async _setState(voidConfigState: VoidConfigState, type: 'usual' | 'initialState' = 'usual') {
|
||||
this.state = voidConfigState
|
||||
if (type === 'usual')
|
||||
this._onDidChangeState.fire()
|
||||
else if (type === 'initialState')
|
||||
this._onDidGetInitState.fire()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(IVoidConfigStateService, VoidConfigStateService, InstantiationType.Eager);
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ export const voidProviderDefaults = {
|
|||
},
|
||||
openAICompatible: {
|
||||
apiKey: '',
|
||||
endpoint: 'http://127.0.0.1:11434/v1',
|
||||
endpoint: 'https://my-website.com/v1',
|
||||
},
|
||||
gemini: {
|
||||
apiKey: '',
|
||||
|
|
@ -47,7 +47,7 @@ export const voidProviderDefaults = {
|
|||
|
||||
export const voidInitModelOptions = {
|
||||
anthropic: () => ({
|
||||
model: 'claude-3-5-sonnet-20240620',
|
||||
// model: 'claude-3-5-sonnet-20240620',
|
||||
models: [
|
||||
'claude-3-5-sonnet-20240620',
|
||||
'claude-3-opus-20240229',
|
||||
|
|
@ -56,7 +56,7 @@ export const voidInitModelOptions = {
|
|||
],
|
||||
}),
|
||||
openAI: () => ({
|
||||
model: 'gpt-4o',
|
||||
// model: 'gpt-4o',
|
||||
models: [
|
||||
'o1-preview',
|
||||
'o1-mini',
|
||||
|
|
@ -78,7 +78,7 @@ export const voidInitModelOptions = {
|
|||
],
|
||||
}),
|
||||
ollama: () => ({ // TODO make this do a fetch to get the models
|
||||
model: 'codestral',
|
||||
// model: 'codestral',
|
||||
models: [
|
||||
'codestral',
|
||||
'qwen2.5-coder',
|
||||
|
|
@ -177,15 +177,15 @@ export const voidInitModelOptions = {
|
|||
],
|
||||
}),
|
||||
openRouter: () => ({
|
||||
model: 'openai/gpt-4o',
|
||||
// model: 'openai/gpt-4o',
|
||||
models: null, // any
|
||||
}),
|
||||
openAICompatible: () => ({
|
||||
model: 'openai/gpt-4o',
|
||||
// model: 'openai/gpt-4o',
|
||||
models: null, // any
|
||||
}),
|
||||
gemini: () => ({
|
||||
model: 'gemini-1.5-flash',
|
||||
// model: 'gemini-1.5-flash',
|
||||
models: [
|
||||
'gemini-1.5-flash',
|
||||
'gemini-1.5-pro',
|
||||
|
|
@ -194,7 +194,7 @@ export const voidInitModelOptions = {
|
|||
],
|
||||
}),
|
||||
groq: () => ({
|
||||
model: 'mixtral-8x7b-32768',
|
||||
// model: 'mixtral-8x7b-32768',
|
||||
models: [
|
||||
"mixtral-8x7b-32768",
|
||||
"llama2-70b-4096",
|
||||
|
|
@ -210,7 +210,8 @@ export const providerNames = Object.keys(voidProviderDefaults) as ProviderName[]
|
|||
|
||||
|
||||
|
||||
export type VoidProviderState = {
|
||||
// state
|
||||
export type SettingsOfProvider = {
|
||||
[providerName in ProviderName]: (
|
||||
{
|
||||
[optionName in keyof typeof voidProviderDefaults[providerName]]: string
|
||||
|
|
@ -221,14 +222,13 @@ export type VoidProviderState = {
|
|||
maxTokens: string,
|
||||
|
||||
models: string[] | null, // if null, user can type in any string as a model
|
||||
model: string,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
type UnionOfKeys<T> = T extends T ? keyof T : never;
|
||||
|
||||
type ProviderSettingName = UnionOfKeys<VoidProviderState[ProviderName]>
|
||||
export type SettingName = UnionOfKeys<SettingsOfProvider[ProviderName]>
|
||||
|
||||
|
||||
|
||||
|
|
@ -238,23 +238,43 @@ type DisplayInfo = {
|
|||
placeholder: string,
|
||||
}
|
||||
|
||||
export const displayInfoOfSettingName = (providerName: ProviderName, settingName: ProviderSettingName): DisplayInfo => {
|
||||
export const titleOfProviderName = (providerName: ProviderName) => {
|
||||
if (providerName === 'anthropic')
|
||||
return 'Anthropic'
|
||||
else if (providerName === 'openAI')
|
||||
return 'OpenAI'
|
||||
else if (providerName === 'ollama')
|
||||
return 'Ollama'
|
||||
else if (providerName === 'openRouter')
|
||||
return 'OpenRouter'
|
||||
else if (providerName === 'openAICompatible')
|
||||
return 'OpenAI-Compatible'
|
||||
else if (providerName === 'gemini')
|
||||
return 'Gemini'
|
||||
else if (providerName === 'groq')
|
||||
return 'Groq'
|
||||
|
||||
throw new Error(`descOfProviderName: Unknown provider name: "${providerName}"`)
|
||||
}
|
||||
|
||||
export const displayInfoOfSettingName = (providerName: ProviderName, settingName: SettingName): DisplayInfo => {
|
||||
if (settingName === 'apiKey') {
|
||||
return {
|
||||
title: 'API Key',
|
||||
type: 'string',
|
||||
placeholder: providerName === 'anthropic' ? 'sk-ant-api03-abc123...' :
|
||||
placeholder: providerName === 'anthropic' ? 'sk-ant-abc123...' : // sk-ant-api03-abc123
|
||||
providerName === 'openAI' ? 'sk-proj-abc123...' :
|
||||
providerName === 'openRouter' ? 'sk-or-v1-abc123...' :
|
||||
providerName === 'openRouter' ? 'sk-or-abc123...' : // sk-or-v1-abc123
|
||||
providerName === 'gemini' ? 'abc123...' :
|
||||
providerName === 'groq' ? 'gsk_abc123...' :
|
||||
'(never)',
|
||||
providerName === 'openAICompatible' ? 'sk-abc123...' :
|
||||
'(never)',
|
||||
}
|
||||
}
|
||||
else if (settingName === 'endpoint') {
|
||||
return {
|
||||
title: providerName === 'ollama' ? 'The endpoint of your Ollama instance.' :
|
||||
providerName === 'openAICompatible' ? 'The baseUrl (excluding /chat/completions).'
|
||||
title: providerName === 'ollama' ? 'Your Ollama endpoint' :
|
||||
providerName === 'openAICompatible' ? 'Endpoint compatible with OpenAI API' // (do not include /chat/completions)
|
||||
: '(never)',
|
||||
type: 'string',
|
||||
placeholder: providerName === 'ollama' || providerName === 'openAICompatible' ?
|
||||
|
|
@ -269,16 +289,9 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName
|
|||
placeholder: '1024',
|
||||
}
|
||||
}
|
||||
else if (settingName === 'model') {
|
||||
return {
|
||||
title: 'Model',
|
||||
type: '(never)',
|
||||
placeholder: '(never)',
|
||||
}
|
||||
}
|
||||
else if (settingName === 'enabled') {
|
||||
return {
|
||||
title: 'Enabled',
|
||||
title: 'Enabled?',
|
||||
type: 'boolean',
|
||||
placeholder: '(never)',
|
||||
}
|
||||
|
|
@ -297,7 +310,7 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName
|
|||
|
||||
|
||||
|
||||
export const defaultVoidProviderState: VoidProviderState = {
|
||||
export const defaultVoidProviderState: SettingsOfProvider = {
|
||||
anthropic: {
|
||||
...voidProviderDefaults.anthropic,
|
||||
...voidInitModelOptions.anthropic(),
|
||||
|
|
@ -344,22 +357,21 @@ export const defaultVoidProviderState: VoidProviderState = {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
type VoidFeatureState = {
|
||||
// this is a state
|
||||
export type ModelSelectionOfFeature = {
|
||||
'Ctrl+L': {
|
||||
provider: ProviderName,
|
||||
model: string,
|
||||
providerName: ProviderName,
|
||||
modelName: string,
|
||||
} | null,
|
||||
'Ctrl+K': {
|
||||
provider: ProviderName,
|
||||
model: string,
|
||||
providerName: ProviderName,
|
||||
modelName: string,
|
||||
} | null,
|
||||
'Autocomplete': {
|
||||
provider: ProviderName,
|
||||
model: string,
|
||||
providerName: ProviderName,
|
||||
modelName: string,
|
||||
} | null,
|
||||
}
|
||||
export type FeatureName = keyof VoidFeatureState
|
||||
export const featureNames = ['Ctrl+L', 'Ctrl+K', 'Autocomplete']
|
||||
export type FeatureName = keyof ModelSelectionOfFeature
|
||||
export const featureNames = ['Ctrl+L', 'Ctrl+K', 'Autocomplete'] as const
|
||||
|
||||
|
|
@ -1,14 +1,27 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import { parseMaxTokensStr, SendLLMMessageFnTypeInternal } from './util.js';
|
||||
import { parseMaxTokensStr } from './util.js';
|
||||
import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js';
|
||||
import { displayInfoOfSettingName } from '../../common/voidConfigTypes.js';
|
||||
|
||||
// Anthropic
|
||||
type LLMMessageAnthropic = {
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
|
||||
const thisConfig = voidConfig.anthropic
|
||||
const thisConfig = settingsOfProvider.anthropic
|
||||
|
||||
const maxTokens = parseMaxTokensStr(thisConfig.maxTokens)
|
||||
if (maxTokens === undefined) {
|
||||
onError({ message: `Please set a value for ${displayInfoOfSettingName('anthropic', 'maxTokens').title}.`, fullError: null })
|
||||
return
|
||||
}
|
||||
|
||||
const anthropic = new Anthropic({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true });
|
||||
|
||||
|
|
@ -21,11 +34,13 @@ export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onTex
|
|||
// remove system messages for Anthropic
|
||||
const anthropicMessages = messages.filter(msg => msg.role !== 'system') as LLMMessageAnthropic[]
|
||||
|
||||
|
||||
|
||||
const stream = anthropic.messages.stream({
|
||||
system: systemMessage,
|
||||
messages: anthropicMessages,
|
||||
model: thisConfig.model,
|
||||
max_tokens: parseMaxTokensStr(thisConfig.maxTokens)!, // this might be undefined, but it will just throw an error for the user to see
|
||||
model: modelName,
|
||||
max_tokens: maxTokens,
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -44,10 +59,10 @@ export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onTex
|
|||
stream.on('error', (error) => {
|
||||
// the most common error will be invalid API key (401), so we handle this with a nice message
|
||||
if (error instanceof Anthropic.APIError && error.status === 401) {
|
||||
onError({ error: 'Invalid API key.' })
|
||||
onError({ message: 'Invalid API key.', fullError: error })
|
||||
}
|
||||
else {
|
||||
onError({ error })
|
||||
onError({ message: error + '', fullError: error }) // anthropic errors can be stringified nicely like this
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Content, GoogleGenerativeAI, GoogleGenerativeAIFetchError } from '@google/generative-ai';
|
||||
import { SendLLMMessageFnTypeInternal } from './util';
|
||||
import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js';
|
||||
|
||||
// Gemini
|
||||
export const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
export const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
|
||||
let fullText = ''
|
||||
|
||||
const thisConfig = voidConfig.gemini
|
||||
const thisConfig = settingsOfProvider.gemini
|
||||
|
||||
const genAI = new GoogleGenerativeAI(thisConfig.apiKey);
|
||||
const model = genAI.getGenerativeModel({ model: thisConfig.model });
|
||||
const model = genAI.getGenerativeModel({ model: modelName });
|
||||
|
||||
// remove system messages that get sent to Gemini
|
||||
// str of all system messages
|
||||
|
|
@ -39,10 +44,10 @@ export const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, on
|
|||
})
|
||||
.catch((error) => {
|
||||
if (error instanceof GoogleGenerativeAIFetchError && error.status === 400) {
|
||||
onError({ error: 'Invalid API key.' });
|
||||
onError({ message: 'Invalid API key.', fullError: null });
|
||||
}
|
||||
else {
|
||||
onError({ error });
|
||||
onError({ message: error + '', fullError: error });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// // Greptile
|
||||
// // https://docs.greptile.com/api-reference/query
|
||||
// // https://docs.greptile.com/quickstart#sample-response-streamed
|
||||
|
||||
// import { SendLLMMessageFnTypeInternal } from './util';
|
||||
// import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js';
|
||||
|
||||
// export const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
// export const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, _setAborter }) => {
|
||||
|
||||
// let fullText = ''
|
||||
|
||||
// const thisConfig = voidConfig.greptile
|
||||
// const thisConfig = settingsOfProvider.greptile
|
||||
|
||||
// fetch('https://api.greptile.com/v2/query', {
|
||||
// method: 'POST',
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import Groq from 'groq-sdk';
|
||||
import { SendLLMMessageFnTypeInternal } from './util';
|
||||
import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js';
|
||||
import { parseMaxTokensStr } from './util.js';
|
||||
|
||||
// Groq
|
||||
export const sendGroqMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
export const sendGroqMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
let fullText = '';
|
||||
|
||||
const thisConfig = voidConfig.groq
|
||||
const thisConfig = settingsOfProvider.groq
|
||||
|
||||
const groq = new Groq({
|
||||
apiKey: thisConfig.apiKey,
|
||||
|
|
@ -16,7 +21,7 @@ export const sendGroqMsg: SendLLMMessageFnTypeInternal = async ({ messages, onTe
|
|||
await groq.chat.completions
|
||||
.create({
|
||||
messages: messages,
|
||||
model: thisConfig.model,
|
||||
model: modelName,
|
||||
stream: true,
|
||||
temperature: 0.7,
|
||||
max_tokens: parseMaxTokensStr(thisConfig.maxTokens),
|
||||
|
|
@ -35,7 +40,7 @@ export const sendGroqMsg: SendLLMMessageFnTypeInternal = async ({ messages, onTe
|
|||
onFinalMessage({ fullText });
|
||||
})
|
||||
.catch(error => {
|
||||
onError({ error });
|
||||
onError({ message: error + '', fullError: error });
|
||||
})
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,23 @@
|
|||
import { Ollama, ErrorResponse } from 'ollama';
|
||||
import { SendLLMMessageFnTypeInternal } from './util';
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Ollama } from 'ollama';
|
||||
import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js';
|
||||
import { parseMaxTokensStr } from './util.js';
|
||||
|
||||
// Ollama
|
||||
export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
|
||||
const thisConfig = voidConfig.ollama
|
||||
const thisConfig = settingsOfProvider.ollama
|
||||
|
||||
let fullText = ''
|
||||
|
||||
const ollama = new Ollama({ host: thisConfig.endpoint })
|
||||
|
||||
ollama.chat({
|
||||
model: thisConfig.model,
|
||||
model: modelName,
|
||||
messages: messages,
|
||||
stream: true,
|
||||
options: { num_predict: parseMaxTokensStr(thisConfig.maxTokens) } // this is max_tokens
|
||||
|
|
@ -30,15 +35,15 @@ export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText,
|
|||
})
|
||||
// when error/fail
|
||||
.catch((error) => {
|
||||
if (typeof error === 'object') {
|
||||
const e = error.error as ErrorResponse['error']
|
||||
if (e) {
|
||||
const name = error.name ?? 'Error'
|
||||
onError({ error: `${name}: ${e}` })
|
||||
return;
|
||||
}
|
||||
}
|
||||
onError({ error })
|
||||
// if (typeof error === 'object') {
|
||||
// const e = error.error as ErrorResponse['error']
|
||||
// if (e) {
|
||||
// const name = error.name ?? 'Error'
|
||||
// onError({ error: `${name}: ${e}` })
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
onError({ message: error + '', fullError: error })
|
||||
})
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import OpenAI from 'openai';
|
||||
import { SendLLMMessageFnTypeInternal } from './util';
|
||||
import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js';
|
||||
import { parseMaxTokensStr } from './util.js';
|
||||
|
||||
|
||||
// OpenAI, OpenRouter, OpenAICompatible
|
||||
export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName }) => {
|
||||
export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }) => {
|
||||
|
||||
let fullText = ''
|
||||
|
||||
|
|
@ -13,12 +18,12 @@ export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText,
|
|||
|
||||
|
||||
if (providerName === 'openAI') {
|
||||
const thisConfig = voidConfig.openAI
|
||||
const thisConfig = settingsOfProvider.openAI
|
||||
openai = new OpenAI({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true });
|
||||
options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) }
|
||||
options = { model: modelName, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) }
|
||||
}
|
||||
else if (providerName === 'openRouter') {
|
||||
const thisConfig = voidConfig.openRouter
|
||||
const thisConfig = settingsOfProvider.openRouter
|
||||
openai = new OpenAI({
|
||||
baseURL: 'https://openrouter.ai/api/v1', apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true,
|
||||
defaultHeaders: {
|
||||
|
|
@ -26,12 +31,12 @@ export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText,
|
|||
'X-Title': 'Void Editor', // Optional. Shows in rankings on openrouter.ai.
|
||||
},
|
||||
});
|
||||
options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) }
|
||||
options = { model: modelName, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) }
|
||||
}
|
||||
else if (providerName === 'openAICompatible') {
|
||||
const thisConfig = voidConfig.openAICompatible
|
||||
const thisConfig = settingsOfProvider.openAICompatible
|
||||
openai = new OpenAI({ baseURL: thisConfig.endpoint, apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true })
|
||||
options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) }
|
||||
options = { model: modelName, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) }
|
||||
}
|
||||
else {
|
||||
console.error(`sendOpenAIMsg: invalid providerName: ${providerName}`)
|
||||
|
|
@ -53,10 +58,10 @@ export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText,
|
|||
// when error/fail - this catches errors of both .create() and .then(for await)
|
||||
.catch(error => {
|
||||
if (error instanceof OpenAI.APIError && error.status === 401) {
|
||||
onError({ error: 'Invalid API key.' });
|
||||
onError({ message: 'Invalid API key.', fullError: error });
|
||||
}
|
||||
else {
|
||||
onError({ error });
|
||||
onError({ message: error, fullError: error });
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SendLLMMMessageParams, OnText, OnFinalMessage, OnError } from '../../common/llmMessageTypes.js';
|
||||
import { IMetricsService } from '../../common/metricsService.js';
|
||||
|
||||
|
|
@ -13,14 +18,14 @@ export const sendLLMMessage = ({
|
|||
onFinalMessage: onFinalMessage_,
|
||||
onError: onError_,
|
||||
abortRef: abortRef_,
|
||||
voidConfig,
|
||||
logging: { loggingName },
|
||||
providerName
|
||||
settingsOfProvider,
|
||||
providerName,
|
||||
modelName,
|
||||
}: SendLLMMMessageParams,
|
||||
|
||||
metricsService: IMetricsService
|
||||
) => {
|
||||
if (!voidConfig) return;
|
||||
|
||||
// trim message content (Anthropic and other providers give an error if there is trailing whitespace)
|
||||
messages = messages.map(m => ({ ...m, content: m.content.trim() }))
|
||||
|
|
@ -54,11 +59,12 @@ export const sendLLMMessage = ({
|
|||
onFinalMessage_({ fullText })
|
||||
}
|
||||
|
||||
const onError: OnError = ({ error }) => {
|
||||
const onError: OnError = ({ message: error, fullError }) => {
|
||||
if (_didAbort) return
|
||||
console.log("ERROR!!!!!", error)
|
||||
console.error('sendLLMMessage onError:', error)
|
||||
captureChatEvent(`${loggingName} - Error`, { error })
|
||||
onError_({ error })
|
||||
onError_({ message: error, fullError })
|
||||
}
|
||||
|
||||
const onAbort = () => {
|
||||
|
|
@ -74,34 +80,31 @@ export const sendLLMMessage = ({
|
|||
try {
|
||||
switch (providerName) {
|
||||
case 'anthropic':
|
||||
sendAnthropicMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
sendAnthropicMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'openAI':
|
||||
case 'openRouter':
|
||||
case 'openAICompatible':
|
||||
sendOpenAIMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
sendOpenAIMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'gemini':
|
||||
sendGeminiMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
sendGeminiMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'ollama':
|
||||
sendOllamaMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
sendOllamaMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
// case 'greptile':
|
||||
// sendGreptileMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
// break;
|
||||
case 'groq':
|
||||
sendGroqMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName });
|
||||
sendGroqMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
default:
|
||||
onError({ error: `Error: whichApi was "${providerName}", which is not recognized!` })
|
||||
onError({ message: `Error: Void provider was "${providerName}", which is not recognized.`, fullError: null })
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
if (error instanceof Error) { onError({ error }) }
|
||||
else { onError({ error: `Unexpected Error in sendLLMMessage: ${error}` }); }
|
||||
if (error instanceof Error) { onError({ message: error + '', fullError: error }) }
|
||||
else { onError({ message: `Unexpected Error in sendLLMMessage: ${error}`, fullError: error }); }
|
||||
// ; (_aborter as any)?.()
|
||||
// _didAbort = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { ProviderName, VoidProviderState } from '../../common/configTypes'
|
||||
import { LLMMessage, OnText, OnFinalMessage, OnError } from '../../common/llmMessageTypes'
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const parseMaxTokensStr = (maxTokensStr: string) => {
|
||||
// parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN
|
||||
|
|
@ -10,13 +12,3 @@ export const parseMaxTokensStr = (maxTokensStr: string) => {
|
|||
}
|
||||
|
||||
|
||||
export type SendLLMMessageFnTypeInternal = (params: {
|
||||
messages: LLMMessage[];
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
voidConfig: VoidProviderState;
|
||||
providerName: ProviderName;
|
||||
|
||||
_setAborter: (aborter: () => void) => void;
|
||||
}) => void
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// registered in app.ts
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js';
|
||||
import { Emitter, Event } from '../../../base/common/event.js';
|
||||
import { listenerNames, ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, ProxyLLMMessageParams, AbortRef, SendLLMMMessageParams, ProxyLLMMessageAbortParams } from '../common/llmMessageTypes.js';
|
||||
import { BlockedProxyParams, ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, ProxyLLMMessageParams, AbortRef, SendLLMMMessageParams, ProxyLLMMessageAbortParams } from '../common/llmMessageTypes.js';
|
||||
import { sendLLMMessage } from './llmMessage/sendLLMMessage.js'
|
||||
import { IMetricsService } from '../common/metricsService.js';
|
||||
|
||||
|
|
@ -28,12 +28,15 @@ export class LLMMessageChannel implements IServerChannel {
|
|||
private readonly _abortRefOfRequestId: Record<string, AbortRef> = {}
|
||||
|
||||
|
||||
// stupidly, channels can't take in @IService
|
||||
constructor(
|
||||
private readonly metricsService: IMetricsService
|
||||
) { }
|
||||
private readonly metricsService: IMetricsService,
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
// browser uses this to listen for changes
|
||||
listen(_: unknown, event: typeof listenerNames[number]): Event<any> {
|
||||
listen(_: unknown, event: BlockedProxyParams): Event<any> {
|
||||
if (event === 'onText') {
|
||||
return this.onText;
|
||||
}
|
||||
|
|
@ -68,7 +71,7 @@ export class LLMMessageChannel implements IServerChannel {
|
|||
}
|
||||
|
||||
// the only place sendLLMMessage is actually called
|
||||
private _callSendLLMMessage(params: ProxyLLMMessageParams) {
|
||||
private async _callSendLLMMessage(params: ProxyLLMMessageParams) {
|
||||
const { requestId } = params;
|
||||
|
||||
if (!(requestId in this._abortRefOfRequestId))
|
||||
|
|
@ -78,7 +81,7 @@ export class LLMMessageChannel implements IServerChannel {
|
|||
...params,
|
||||
onText: ({ newText, fullText }) => { this._onText.fire({ requestId, newText, fullText }); },
|
||||
onFinalMessage: ({ fullText }) => { this._onFinalMessage.fire({ requestId, fullText }); },
|
||||
onError: ({ error }) => { this._onError.fire({ requestId, error }); },
|
||||
onError: ({ message: error, fullError }) => { console.log('sendLLM: firing err'); this._onError.fire({ requestId, message: error, fullError }); },
|
||||
abortRef: this._abortRefOfRequestId[requestId],
|
||||
}
|
||||
sendLLMMessage(mainThreadParams, this.metricsService);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from '../../../base/common/lifecycle.js';
|
||||
|
|
@ -10,18 +10,12 @@ import { IMetricsService } from '../common/metricsService.js';
|
|||
import { PostHog } from 'posthog-node'
|
||||
|
||||
|
||||
// posthog-js (old):
|
||||
// posthog.init('phc_UanIdujHiLp55BkUTjB1AuBXcasVkdqRwgnwRlWESH2', { api_host: 'https://us.i.posthog.com', })
|
||||
|
||||
// const buildEnv = 'development';
|
||||
// const buildNumber = '1.0.0';
|
||||
// const isMac = process.platform === 'darwin';
|
||||
// // TODO use commandKey
|
||||
// const commandKey = isMac ? '⌘' : 'Ctrl';
|
||||
// const systemInfo = {
|
||||
// buildEnv,
|
||||
// buildNumber,
|
||||
// isMac,
|
||||
// }
|
||||
|
||||
export class MetricsMainService extends Disposable implements IMetricsService {
|
||||
_serviceBrand: undefined;
|
||||
|
|
@ -43,9 +37,9 @@ export class MetricsMainService extends Disposable implements IMetricsService {
|
|||
}
|
||||
|
||||
capture: IMetricsService['capture'] = (event, params) => {
|
||||
console.log('Capturing', { event, params })
|
||||
console.log('full capture:', { distinctId: this._distinctId, event, properties: params })
|
||||
this.client.capture({ distinctId: this._distinctId, event, properties: params })
|
||||
const capture = { distinctId: this._distinctId, event, properties: params } as const
|
||||
// console.log('full capture:', capture)
|
||||
this.client.capture(capture)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -742,18 +742,6 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
|
|||
cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) });
|
||||
});
|
||||
|
||||
|
||||
// // Void: send from https://
|
||||
// this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, async (details, cb) => {
|
||||
// // const voidConfig = this.voidConfigStateService.state.voidConfig
|
||||
// // const whichApi = voidConfig.default['whichApi']
|
||||
// const endpoint = 'http://127.' //string | undefined = voidConfig[whichApi as VoidConfigField].endpoint
|
||||
|
||||
// if (endpoint && details.url.startsWith(endpoint)) {
|
||||
// details.requestHeaders['Origin'] = 'https://app.voideditor.com'
|
||||
// }
|
||||
// cb({ cancel: false, requestHeaders: details.requestHeaders });
|
||||
// });
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { diffLines } from './react/out/util/diffLines.js'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { OperatingSystem, OS } from '../../../../base/common/platform.js';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
.monaco-editor .void-sweepIdxBG {
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-editor .void-sweepIdxBG {
|
||||
background-color: var(--vscode-void-sweepIdxBG);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CodeSelection } from '../registerThreads.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// // used for ctrl+l
|
||||
// const partialGenerationInstructions = ``
|
||||
|
|
@ -9,6 +11,31 @@
|
|||
// const fimInstructions = ``
|
||||
|
||||
|
||||
// CTRL+K prompt:
|
||||
// const promptContent = `Here is the user's original selection:
|
||||
// \`\`\`
|
||||
// <MID>${selection}</MID>
|
||||
// \`\`\`
|
||||
|
||||
// The user wants to apply the following instructions to the selection:
|
||||
// ${instructions}
|
||||
|
||||
// Please rewrite the selection following the user's instructions.
|
||||
|
||||
// Instructions to follow:
|
||||
// 1. Follow the user's instructions
|
||||
// 2. You may ONLY CHANGE the selection, and nothing else in the file
|
||||
// 3. Make sure all brackets in the new selection are balanced the same was as in the original selection
|
||||
// 3. Be careful not to duplicate or remove variables, comments, or other syntax by mistake
|
||||
|
||||
// Complete the following:
|
||||
// \`\`\`
|
||||
// <PRE>${prefix}</PRE>
|
||||
// <SUF>${suffix}</SUF>
|
||||
// <MID>`;
|
||||
|
||||
|
||||
|
||||
export const generateDiffInstructions = `
|
||||
You are a coding assistant. You are given a list of relevant files \`files\`, a selection that the user is making \`selection\`, and instructions to follow \`instructions\`.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
// clear temp dirs
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { ReactNode } from "react"
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import { atomOneDarkReasonable } from "react-syntax-highlighter/dist/esm/styles/hljs";
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { JSX, useCallback, useEffect, useState } from 'react'
|
||||
import { marked, MarkedToken, Token } from 'marked'
|
||||
import { BlockCode } from './BlockCode.js'
|
||||
|
|
@ -44,7 +49,7 @@ const CodeButtonsOnHover = ({ diffRepr: text }: { diffRepr: string }) => {
|
|||
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||
onClick={async () => {
|
||||
|
||||
inlineDiffService.startStreaming({ type: 'ctrl+l', providerName: 'anthropic' }, text)
|
||||
inlineDiffService.startStreaming({ featureName: 'Ctrl+L' }, text)
|
||||
}}
|
||||
>
|
||||
Apply
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
||||
import { ErrorDisplay } from './ErrorDisplay.js';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
fallback?: ReactNode;
|
||||
onDismiss?: () => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean;
|
||||
error: Error | null;
|
||||
errorInfo: ErrorInfo | null;
|
||||
}
|
||||
|
||||
class ErrorBoundary extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
hasError: false,
|
||||
error: null,
|
||||
errorInfo: null
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): Partial<State> {
|
||||
return {
|
||||
hasError: true,
|
||||
error
|
||||
};
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
|
||||
this.setState({
|
||||
error,
|
||||
errorInfo
|
||||
});
|
||||
}
|
||||
|
||||
render(): ReactNode {
|
||||
if (this.state.hasError && this.state.error) {
|
||||
// If a custom fallback is provided, use it
|
||||
if (this.props.fallback) {
|
||||
return this.props.fallback;
|
||||
}
|
||||
|
||||
// Use ErrorDisplay component as the default error UI
|
||||
return (
|
||||
<ErrorDisplay
|
||||
message={this.state.error + ''}
|
||||
fullError={this.state.error}
|
||||
onDismiss={this.props.onDismiss || null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
|
|
@ -1,83 +1,37 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { AlertCircle, ChevronDown, ChevronUp, X } from 'lucide-react';
|
||||
|
||||
|
||||
// const opaqueMessage = `\
|
||||
// Unfortunately, Void can't see the full error. However, you should be able to find more details by pressing ${getCmdKey()}+Shift+P, typing "Toggle Developer Tools", and looking at the console.\n
|
||||
// This error often means you have an incorrect API key. If you're self-hosting your own server, it might mean your CORS headers are off, and you should make sure your server's response has the header "Access-Control-Allow-Origins" set to "*", or at least allows "vscode-file://vscode-app".`
|
||||
// if ((error instanceof Error) && (error.cause + '').includes('TypeError: Failed to fetch')) {
|
||||
// e = error as any
|
||||
// e['Void Team'] = opaqueMessage
|
||||
// }
|
||||
|
||||
|
||||
type Details = {
|
||||
message: string,
|
||||
name: string,
|
||||
stack: string | null,
|
||||
cause: string | null,
|
||||
code: string | null,
|
||||
additional: Record<string, any>
|
||||
}
|
||||
|
||||
// Get detailed error information
|
||||
const getErrorDetails = (error: unknown) => {
|
||||
|
||||
let details: Details;
|
||||
|
||||
let e: Error & { [other: string]: undefined | any }
|
||||
|
||||
// If fetch() fails, it gives an opaque message. We add extra details to the error.
|
||||
if (error instanceof Error) {
|
||||
e = error
|
||||
}
|
||||
// sometimes error is an object but not an Error
|
||||
else if (typeof error === 'object') {
|
||||
e = new Error(`More details below.`, { cause: JSON.stringify(error) })
|
||||
|
||||
}
|
||||
else {
|
||||
e = new Error(String(error))
|
||||
}
|
||||
// console.log('error display', JSON.stringify(e))
|
||||
|
||||
const message = e.message && e.error ?
|
||||
(e.message + ':\n' + e.error)
|
||||
: e.message || e.error || JSON.stringify(error)
|
||||
|
||||
details = {
|
||||
name: e.name || 'Error',
|
||||
message: message,
|
||||
stack: null, // e.stack is ignored because it's ugly and not very useful
|
||||
cause: e.cause ? String(e.cause) : null,
|
||||
code: e.code || null,
|
||||
additional: {}
|
||||
}
|
||||
|
||||
|
||||
// Collect any additional properties from the e
|
||||
for (let prop of Object.getOwnPropertyNames(e).filter((prop) => !Object.keys(details).includes(prop)))
|
||||
details.additional[prop] = (e as any)[prop]
|
||||
|
||||
return details;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const ErrorDisplay = ({
|
||||
error,
|
||||
onDismiss = null,
|
||||
showDismiss = true,
|
||||
message,
|
||||
fullError,
|
||||
onDismiss,
|
||||
showDismiss,
|
||||
}: {
|
||||
error: Error | object | string,
|
||||
message: string,
|
||||
fullError: Error | null,
|
||||
onDismiss: (() => void) | null,
|
||||
showDismiss?: boolean,
|
||||
className?: string
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const details = getErrorDetails(error);
|
||||
const hasDetails = details.cause || Object.keys(details.additional).length > 0;
|
||||
let details: string | null = null;
|
||||
|
||||
if (fullError === null) {
|
||||
details = null
|
||||
}
|
||||
else if (typeof fullError === 'object') {
|
||||
details = JSON.stringify(fullError, null, 2)
|
||||
}
|
||||
else if (typeof fullError === 'string') {
|
||||
details = null
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className={`rounded-lg border border-red-200 bg-red-50 p-4 overflow-auto`}>
|
||||
|
|
@ -87,16 +41,18 @@ export const ErrorDisplay = ({
|
|||
<AlertCircle className="h-5 w-5 text-red-500 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-red-800">
|
||||
{details.name}
|
||||
{/* eg Error */}
|
||||
Error
|
||||
</h3>
|
||||
<p className="text-red-700 mt-1">
|
||||
{details.message}
|
||||
{/* eg Something went wrong */}
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
{hasDetails && (
|
||||
{details && (
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="text-red-600 hover:text-red-800 p-1 rounded"
|
||||
|
|
@ -120,38 +76,12 @@ export const ErrorDisplay = ({
|
|||
</div>
|
||||
|
||||
{/* Expandable Details */}
|
||||
{isExpanded && hasDetails && (
|
||||
{isExpanded && details && (
|
||||
<div className="mt-4 space-y-3 border-t border-red-200 pt-3">
|
||||
{details.code && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Error Code: </span>
|
||||
<span className="text-red-700">{details.code}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{details.cause && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Cause: </span>
|
||||
<span className="text-red-700">{details.cause}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{Object.keys(details.additional).length > 0 && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Additional Information:</span>
|
||||
<pre className="mt-1 text-sm text-red-700 overflow-x-auto whitespace-pre-wrap">
|
||||
{Object.keys(details.additional).map(key => `${key}:\n${details.additional[key]}`).join('\n')}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
{/* {details.stack && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Stack Trace:</span>
|
||||
<pre className="mt-1 text-sm text-red-700 overflow-x-auto whitespace-pre-wrap">
|
||||
{details.stack}
|
||||
</pre>
|
||||
</div>
|
||||
)} */}
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Full Error: </span>
|
||||
<pre className="text-red-700">{details}</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { FeatureName, featureNames, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js'
|
||||
import { useConfigState, useService } from '../util/services.js'
|
||||
import ErrorBoundary from './ErrorBoundary.js'
|
||||
import { VoidSelectBox } from './inputs.js'
|
||||
import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js'
|
||||
|
||||
|
||||
|
||||
|
||||
export const ModelSelectionOfFeature = ({ featureName }: { featureName: FeatureName }) => {
|
||||
|
||||
const voidConfigService = useService('configStateService')
|
||||
const voidConfigState = useConfigState()
|
||||
|
||||
const modelOptions: { text: string, value: [string, string] }[] = []
|
||||
|
||||
modelOptions.push({ text: 'Select a Provider', value: ['MyProvider', 'MyModel'] })
|
||||
|
||||
for (const providerName of providerNames) {
|
||||
const providerConfig = voidConfigState[providerName]
|
||||
if (providerConfig.enabled !== 'true') continue
|
||||
providerConfig.models?.forEach(model => {
|
||||
modelOptions.push({ text: `${providerName} - ${model}`, value: [providerName, model] })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
return <>
|
||||
<h2>{featureName}</h2>
|
||||
{
|
||||
<VoidSelectBox
|
||||
options={modelOptions}
|
||||
onChangeSelection={(newVal) => { voidConfigService.setModelSelectionOfFeature(featureName, { providerName: newVal[0] as ProviderName, modelName: newVal[1] }) }}
|
||||
// we are responsible for setting the initial state here
|
||||
onCreateInstance={useCallback((instance: SelectBox) => {
|
||||
const updateState = () => {
|
||||
const settingsAtProvider = voidConfigService.state.modelSelectionOfFeature[featureName]
|
||||
const index = modelOptions.findIndex(v => v.value[0] === settingsAtProvider?.providerName && v.value[1] === settingsAtProvider?.modelName)
|
||||
if (index !== -1)
|
||||
instance.select(index)
|
||||
}
|
||||
updateState()
|
||||
const disposable = voidConfigService.onDidGetInitState(updateState)
|
||||
return [disposable]
|
||||
}, [voidConfigService, modelOptions, featureName])}
|
||||
/>}
|
||||
|
||||
{/* <h1>Settings - {featureName}</h1> */}
|
||||
{/* {models.map(([providerName, model], i) => <p key={i}>{providerName} - {model}</p>)} */}
|
||||
</>
|
||||
}
|
||||
|
||||
export const ModelSelectionSettings = () => {
|
||||
return <>
|
||||
{featureNames.map(featureName => <ModelSelectionOfFeature
|
||||
key={featureName}
|
||||
featureName={featureName}
|
||||
/>)}
|
||||
</>
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { mountFnGenerator } from '../util/mountFnGenerator.js'
|
||||
|
|
@ -15,8 +15,9 @@ import { useSidebarState } from '../util/services.js';
|
|||
import '../styles.css'
|
||||
import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
import { SidebarChat } from './SidebarChat.js';
|
||||
import { SidebarModelSettings } from './SidebarModelSettings.js';
|
||||
import { SidebarProviderSettings } from './SidebarProviderSettings.js';
|
||||
import { ModelSelectionSettings } from './ModelSelectionSettings.js';
|
||||
import { VoidProviderSettings } from './VoidProviderSettings.js';
|
||||
import ErrorBoundary from './ErrorBoundary.js';
|
||||
|
||||
const Sidebar = () => {
|
||||
const sidebarState = useSidebarState()
|
||||
|
|
@ -33,17 +34,25 @@ const Sidebar = () => {
|
|||
}}>clickme {tab}</span> */}
|
||||
|
||||
<div className={`mb-2 ${isHistoryOpen ? '' : 'hidden'}`}>
|
||||
<SidebarThreadSelector />
|
||||
<ErrorBoundary>
|
||||
<SidebarThreadSelector />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
|
||||
<div className={`${tab === 'chat' ? '' : 'hidden'}`}>
|
||||
<SidebarChat />
|
||||
<ErrorBoundary>
|
||||
<SidebarChat />
|
||||
</ErrorBoundary>
|
||||
|
||||
<ErrorBoundary>
|
||||
<ModelSelectionSettings />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
|
||||
<div className={`${tab === 'settings' ? '' : 'hidden'}`}>
|
||||
<SidebarModelSettings />
|
||||
--------
|
||||
<SidebarProviderSettings />
|
||||
<ErrorBoundary>
|
||||
<VoidProviderSettings />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { FormEvent, Fragment, useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
|
||||
|
|
@ -17,9 +18,9 @@ import { URI } from '../../../../../../../base/common/uri.js';
|
|||
import { EndOfLinePreference } from '../../../../../../../editor/common/model.js';
|
||||
import { IDisposable } from '../../../../../../../base/common/lifecycle.js';
|
||||
import { ErrorDisplay } from './ErrorDisplay.js';
|
||||
import { LLMMessageServiceParams } from '../../../../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { LLMMessageServiceParams, OnError } from '../../../../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { getCmdKey } from '../../../getCmdKey.js'
|
||||
import { HistoryInputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
|
||||
import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
|
||||
import { VoidInputBox } from './inputs.js';
|
||||
|
||||
|
||||
|
|
@ -173,7 +174,9 @@ export const SelectedFiles = (
|
|||
}
|
||||
|
||||
|
||||
const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
||||
const ChatBubble = ({ chatMessage }: {
|
||||
chatMessage: ChatMessage
|
||||
}) => {
|
||||
|
||||
const role = chatMessage.role
|
||||
|
||||
|
|
@ -203,7 +206,7 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
|||
|
||||
export const SidebarChat = () => {
|
||||
|
||||
const chatInputRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
const inputBoxRef: React.MutableRefObject<InputBox | null> = useRef(null);
|
||||
|
||||
const modelService = useService('modelService')
|
||||
|
||||
|
|
@ -213,11 +216,11 @@ export const SidebarChat = () => {
|
|||
useEffect(() => {
|
||||
const disposables: IDisposable[] = []
|
||||
disposables.push(
|
||||
sidebarStateService.onDidFocusChat(() => { chatInputRef.current?.focus() }),
|
||||
sidebarStateService.onDidBlurChat(() => { chatInputRef.current?.blur() })
|
||||
sidebarStateService.onDidFocusChat(() => { inputBoxRef.current?.focus() }),
|
||||
sidebarStateService.onDidBlurChat(() => { inputBoxRef.current?.blur() })
|
||||
)
|
||||
return () => disposables.forEach(d => d.dispose())
|
||||
}, [sidebarStateService, chatInputRef])
|
||||
}, [sidebarStateService, inputBoxRef])
|
||||
|
||||
// config state
|
||||
const voidConfigState = useConfigState()
|
||||
|
|
@ -233,7 +236,7 @@ export const SidebarChat = () => {
|
|||
const [isLoading, setIsLoading] = useState(false)
|
||||
const latestRequestIdRef = useRef<string | null>(null)
|
||||
|
||||
const [latestError, setLatestError] = useState<Error | string | null>(null)
|
||||
const [latestError, setLatestError] = useState<Parameters<OnError>[0] | null>(null)
|
||||
|
||||
const sendLLMMessageService = useService('sendLLMMessageService')
|
||||
|
||||
|
|
@ -242,7 +245,7 @@ export const SidebarChat = () => {
|
|||
const onChangeText = useCallback((newStr: string) => { setInstructions(newStr) }, [setInstructions])
|
||||
const isDisabled = !instructions
|
||||
const formRef = useRef<HTMLFormElement | null>(null)
|
||||
const inputBoxRef: React.MutableRefObject<HistoryInputBox | null> = useRef(null);
|
||||
|
||||
|
||||
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
|
||||
|
|
@ -283,6 +286,13 @@ export const SidebarChat = () => {
|
|||
const currentThread = threadsStateService.getCurrentThread(threadsStateService.state) // the the instant state right now, don't wait for the React state
|
||||
|
||||
// send message to LLM
|
||||
setIsLoading(true) // must come before message is sent so onError will work
|
||||
setLatestError(null)
|
||||
if (inputBoxRef.current) {
|
||||
inputBoxRef.current.value = ''; // this triggers onDidChangeText
|
||||
inputBoxRef.current.blur();
|
||||
}
|
||||
|
||||
const object: LLMMessageServiceParams = {
|
||||
logging: { loggingName: 'Chat' },
|
||||
messages: [...(currentThread?.messages ?? []).map(m => ({ role: m.role, content: m.content || '(null)' })),],
|
||||
|
|
@ -296,8 +306,8 @@ export const SidebarChat = () => {
|
|||
setMessageStream(null)
|
||||
setIsLoading(false)
|
||||
},
|
||||
onError: ({ error }) => {
|
||||
console.log('chat: running error', error)
|
||||
onError: ({ message, fullError }) => {
|
||||
console.log('chat: running error', message, fullError)
|
||||
|
||||
// add assistant's message to chat history, and clear selection
|
||||
let content = messageStream ?? ''; // just use the current content
|
||||
|
|
@ -307,23 +317,16 @@ export const SidebarChat = () => {
|
|||
setMessageStream('')
|
||||
setIsLoading(false)
|
||||
|
||||
setLatestError(error)
|
||||
setLatestError({ message, fullError })
|
||||
},
|
||||
voidConfig: voidConfigState,
|
||||
providerName: 'anthropic',
|
||||
featureName: 'Ctrl+L',
|
||||
|
||||
}
|
||||
|
||||
const latestRequestId = sendLLMMessageService.sendLLMMessage(object)
|
||||
latestRequestIdRef.current = latestRequestId
|
||||
|
||||
|
||||
setIsLoading(true)
|
||||
if (inputBoxRef.current) {
|
||||
inputBoxRef.current.value = ''; // this triggers onDidChangeText
|
||||
inputBoxRef.current.blur();
|
||||
}
|
||||
threadsStateService.setStaging([]) // clear staging
|
||||
setLatestError(null)
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -373,7 +376,8 @@ export const SidebarChat = () => {
|
|||
{/* error message */}
|
||||
{latestError === null ? null :
|
||||
<ErrorDisplay
|
||||
error={latestError}
|
||||
message={latestError.message}
|
||||
fullError={latestError.fullError}
|
||||
onDismiss={() => { setLatestError(null) }}
|
||||
/>
|
||||
}
|
||||
|
|
@ -395,7 +399,6 @@ export const SidebarChat = () => {
|
|||
onChangeText={onChangeText}
|
||||
inputBoxRef={inputBoxRef}
|
||||
multiline={true}
|
||||
initVal=''
|
||||
/>
|
||||
|
||||
{/* submit/stop button */}
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { FeatureName, featureNames, providerNames } from '../../../../../../../platform/void/common/configTypes.js'
|
||||
import { useConfigState } from '../util/services.js'
|
||||
|
||||
|
||||
|
||||
|
||||
export const SidebarModelSettingsForFeature = ({ featureName }: { featureName: FeatureName }) => {
|
||||
|
||||
const voidConfigState = useConfigState()
|
||||
|
||||
const models: [string,string][] = []
|
||||
for (const providerName of providerNames) {
|
||||
const providerConfig = voidConfigState[providerName]
|
||||
if (providerConfig.enabled === 'false') continue
|
||||
providerConfig.models?.forEach(model => {
|
||||
models.push([providerName,model])
|
||||
})
|
||||
}
|
||||
|
||||
return <>
|
||||
<h1>Settings - {featureName}</h1>
|
||||
{models.map(([providerName,model], i) => <span key={i}>{providerName} - {model}</span>)}
|
||||
</>
|
||||
}
|
||||
|
||||
export const SidebarModelSettings = () => {
|
||||
return <>
|
||||
{featureNames.map(featureName => <SidebarModelSettingsForFeature key={featureName} featureName={featureName} />)}
|
||||
</>
|
||||
}
|
||||
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { Fragment } from 'react'
|
||||
import { displayInfoOfSettingName, ProviderName, providerNames } from '../../../../../../../platform/void/common/configTypes.js'
|
||||
import { VoidCheckBox, VoidInputBox, VoidSelectBox } from './inputs.js'
|
||||
import { useConfigState, useService } from '../util/services.js'
|
||||
|
||||
const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => {
|
||||
const voidConfigState = useConfigState()
|
||||
const voidConfigService = useService('configStateService')
|
||||
const { models, model, ...others } = voidConfigState[providerName]
|
||||
|
||||
return <>
|
||||
<h1>{providerName}</h1>
|
||||
|
||||
{/* other settings (e.g. api key) */}
|
||||
{Object.entries(others).map(([settingName, defaultVal], i) => {
|
||||
const sName = settingName as keyof typeof others
|
||||
|
||||
const { title, type, placeholder } = displayInfoOfSettingName(providerName, sName)
|
||||
|
||||
return <Fragment key={i}>
|
||||
<h2>{title}</h2>
|
||||
{
|
||||
type === 'boolean' ?
|
||||
<VoidCheckBox
|
||||
initVal={defaultVal === 'true'}
|
||||
onChangeChecked={(newVal) => { voidConfigService.setState(providerName, sName, newVal ? 'true' : 'false') }}
|
||||
label={settingName}
|
||||
checkboxRef={{ current: null }}
|
||||
/>
|
||||
:
|
||||
<VoidInputBox
|
||||
initVal={defaultVal}
|
||||
placeholder={placeholder}
|
||||
onChangeText={(newVal) => { () => { voidConfigService.setState(providerName, sName, newVal) } }}
|
||||
multiline={false}
|
||||
inputBoxRef={{ current: null }}
|
||||
/>}
|
||||
</Fragment>
|
||||
})}
|
||||
|
||||
<h2>{'Models'}</h2>
|
||||
{models === null ?
|
||||
<p>{'No models available.'}</p>
|
||||
: <VoidSelectBox
|
||||
initVal={models[0]}
|
||||
options={models}
|
||||
onChangeSelection={(newVal) => { () => { } }}
|
||||
selectBoxRef={{ current: null }}
|
||||
/>}
|
||||
|
||||
|
||||
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
export const SidebarProviderSettings = () => {
|
||||
|
||||
return <>
|
||||
{providerNames.map(providerName =>
|
||||
<SettingsForProvider key={providerName} providerName={providerName} />
|
||||
)}
|
||||
|
||||
|
||||
</>
|
||||
}
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
// /*---------------------------------------------------------------------------------------------
|
||||
// * Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
// * Void Editor additions licensed under the AGPLv3 License.
|
||||
// *--------------------------------------------------------------------------------------------*/
|
||||
// import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
// import { useConfigState, useService } from '../util/services.js';
|
||||
|
||||
// import { HistoryInputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
|
||||
// import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js';
|
||||
// import { ConfigState, IVoidConfigStateService } from '../../../registerConfig.js';
|
||||
// import { nonDefaultConfigFields, VoidConfigField } from '../../../../../../../platform/void/common/configTypes.js';
|
||||
|
||||
|
||||
// const SettingOfFieldAndParam = ({ field, param, configState, configStateService }:
|
||||
// { field: VoidConfigField; param: string; configState: ConfigState; configStateService: IVoidConfigStateService }) => {
|
||||
|
||||
// const { partialVoidConfig } = configState
|
||||
|
||||
|
||||
// const { enumArr, defaultVal, description } = configStateService.voidConfigInfo[field][param]
|
||||
// const val = partialVoidConfig[field]?.[param] ?? defaultVal // current value of this item
|
||||
// const initValRef = useRef(val)
|
||||
|
||||
// const updateState = useCallback((newValue: string) => {
|
||||
// configStateService.setField(field, param, newValue)
|
||||
// }, [configStateService, field, param])
|
||||
|
||||
|
||||
// const inputBoxRef = useRef<HistoryInputBox | null>(null);
|
||||
// const selectBoxRef = useRef<SelectBox | null>(null);
|
||||
// const forceState = useCallback((newValue: string) => {
|
||||
// if (inputBoxRef.current) {
|
||||
// inputBoxRef.current.value = newValue;
|
||||
// }
|
||||
// if (selectBoxRef.current) {
|
||||
// selectBoxRef.current.select(enumArr?.indexOf(newValue) ?? 0);
|
||||
// }
|
||||
// // updateState is called automatically when the change happens
|
||||
// }, [enumArr, updateState])
|
||||
|
||||
|
||||
// const resetButton = <button
|
||||
// disabled={val === defaultVal}
|
||||
// title={val === defaultVal ? 'This is the default value.' : `Revert value to '${defaultVal}'?`}
|
||||
// className='group btn btn-sm disabled:opacity-75 disabled:cursor-default'
|
||||
// onClick={() => forceState(defaultVal)}
|
||||
// >
|
||||
// <svg
|
||||
// className='size-5 group-disabled:stroke-current group-disabled:fill-current group-hover:stroke-red-600 group-hover:fill-red-600 duration-200'
|
||||
// fill='currentColor' strokeWidth='0' viewBox='0 0 16 16' height='200px' width='200px' xmlns='http://www.w3.org/2000/svg'><path fillRule='evenodd' clipRule='evenodd' d='M3.5 2v3.5L4 6h3.5V5H4.979l.941-.941a3.552 3.552 0 1 1 5.023 5.023L5.746 14.28l.72.72 5.198-5.198A4.57 4.57 0 0 0 5.2 3.339l-.7.7V2h-1z'></path>
|
||||
// </svg>
|
||||
// </button>
|
||||
|
||||
|
||||
// const inputElement = enumArr === undefined ?
|
||||
// // string
|
||||
// // (<VoidInputBox
|
||||
// // onChangeText={updateState}
|
||||
// // initVal={initValRef.current}
|
||||
// // multiline={false}
|
||||
// // placeholder=''
|
||||
// // inputBoxRef={inputBoxRef}
|
||||
// // />)
|
||||
// <input
|
||||
// className='input p-1 w-full'
|
||||
// type='text'
|
||||
// value={val}
|
||||
// onChange={(e) => updateState(e.target.value)}
|
||||
// />
|
||||
// :
|
||||
// // enum
|
||||
// // (<VoidSelectBox
|
||||
// // onChangeSelection={updateState}
|
||||
// // initVal={initValRef.current}
|
||||
// // options={enumArr}
|
||||
// // selectBoxRef={selectBoxRef}
|
||||
// // />)
|
||||
// (<select
|
||||
// className='dropdown p-1 w-full'
|
||||
// value={val}
|
||||
// onChange={(e) => updateState(e.target.value)}
|
||||
// >
|
||||
// {enumArr.map((option) => (
|
||||
// <option key={option} value={option}>
|
||||
// {option}
|
||||
// </option>
|
||||
// ))}
|
||||
// </select>)
|
||||
|
||||
// return <div>
|
||||
// <label className='hidden'>{param}</label>
|
||||
// <span>{description}</span>
|
||||
// <div className='flex items-center'>
|
||||
// {inputElement}
|
||||
// {resetButton}
|
||||
// </div>
|
||||
// </div>
|
||||
// }
|
||||
|
||||
|
||||
// export const SidebarSettings = () => {
|
||||
|
||||
// const configState = useConfigState()
|
||||
// const configStateService = useService('configStateService')
|
||||
|
||||
// const { voidConfig } = configState
|
||||
// const current_field = voidConfig.default['whichApi'] as VoidConfigField
|
||||
|
||||
// return (
|
||||
// <div className='space-y-4 py-2 overflow-y-auto'>
|
||||
|
||||
// {/* choose the field */}
|
||||
// <div className='outline-vscode-input-bg'>
|
||||
// <SettingOfFieldAndParam
|
||||
// configState={configState}
|
||||
// configStateService={configStateService}
|
||||
// field='default'
|
||||
// param='whichApi'
|
||||
// />
|
||||
// <SettingOfFieldAndParam
|
||||
// configState={configState}
|
||||
// configStateService={configStateService}
|
||||
// field='default'
|
||||
// param='maxTokens'
|
||||
// />
|
||||
// </div>
|
||||
|
||||
// <hr />
|
||||
|
||||
// {/* render all fields, but hide the ones not visible for fast tab switching */}
|
||||
// {nonDefaultConfigFields.map(field => {
|
||||
// return <div
|
||||
// key={field}
|
||||
// className={`flex flex-col gap-y-2 ${field !== current_field ? 'hidden' : ''}`}
|
||||
// >
|
||||
// {Object.keys(configStateService.voidConfigInfo[field]).map((param) => (
|
||||
// <SettingOfFieldAndParam
|
||||
// key={param}
|
||||
// configState={configState}
|
||||
// configStateService={configStateService}
|
||||
// field={field}
|
||||
// param={param}
|
||||
// />
|
||||
// ))}
|
||||
// </div>
|
||||
// })}
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React from "react";
|
||||
import { useService, useThreadsState } from '../util/services.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { titleOfProviderName, displayInfoOfSettingName, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js'
|
||||
import { VoidInputBox } from './inputs.js'
|
||||
import { useConfigState, useService } from '../util/services.js'
|
||||
import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'
|
||||
import ErrorBoundary from './ErrorBoundary.js'
|
||||
|
||||
|
||||
const Setting = ({ providerName, settingName }: { providerName: ProviderName, settingName: any }) => {
|
||||
|
||||
const { title, type, placeholder } = displayInfoOfSettingName(providerName, settingName)
|
||||
const voidConfigService = useService('configStateService')
|
||||
|
||||
|
||||
return <><ErrorBoundary>
|
||||
<label>{title}</label>
|
||||
<VoidInputBox
|
||||
placeholder={placeholder}
|
||||
onChangeText={useCallback((newVal) => {
|
||||
voidConfigService.setSettingOfProvider(providerName, settingName, newVal)
|
||||
}, [voidConfigService, providerName, settingName])}
|
||||
|
||||
// we are responsible for setting the initial value here
|
||||
onCreateInstance={useCallback((instance: InputBox) => {
|
||||
const updateInstanceState = () => {
|
||||
const settingsAtProvider = voidConfigService.state.settingsOfProvider[providerName];
|
||||
// @ts-ignore
|
||||
const stateVal = settingsAtProvider[settingName]
|
||||
instance.value = stateVal
|
||||
}
|
||||
updateInstanceState()
|
||||
const disposable = voidConfigService.onDidGetInitState(updateInstanceState)
|
||||
return [disposable]
|
||||
}, [voidConfigService, providerName, settingName])}
|
||||
multiline={false}
|
||||
/>
|
||||
</ErrorBoundary></>
|
||||
|
||||
}
|
||||
|
||||
|
||||
const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => {
|
||||
const voidConfigState = useConfigState()
|
||||
const { models, ...others } = voidConfigState[providerName]
|
||||
return <>
|
||||
<h1 className='text-xl'>{titleOfProviderName(providerName)}</h1>
|
||||
{/* settings besides models (e.g. api key) */}
|
||||
{Object.keys(others).map((settingName, i) => {
|
||||
return <Setting key={settingName} providerName={providerName} settingName={settingName} />
|
||||
})}
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
export const VoidProviderSettings = () => {
|
||||
|
||||
return <>
|
||||
{providerNames.map(providerName =>
|
||||
<SettingsForProvider key={providerName} providerName={providerName} />
|
||||
)}
|
||||
|
||||
|
||||
</>
|
||||
}
|
||||
|
|
@ -1,29 +1,56 @@
|
|||
import React, { useEffect, useRef } from 'react';
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import { useService } from '../util/services.js';
|
||||
import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
|
||||
import { defaultCheckboxStyles, defaultInputBoxStyles, defaultToggleStyles } from '../../../../../../../platform/theme/browser/defaultStyles.js';
|
||||
import { defaultCheckboxStyles, defaultInputBoxStyles, defaultSelectBoxStyles, defaultToggleStyles } from '../../../../../../../platform/theme/browser/defaultStyles.js';
|
||||
import { SelectBox, unthemedSelectBoxStyles } from '../../../../../../../base/browser/ui/selectBox/selectBox.js';
|
||||
import { Checkbox, Toggle } from '../../../../../../../base/browser/ui/toggle/toggle.js';
|
||||
import { IDisposable } from '../../../../../../../base/common/lifecycle.js';
|
||||
|
||||
// settingitem
|
||||
|
||||
export const VoidInputBox = ({ onChangeText, initVal, placeholder, inputBoxRef, multiline }: {
|
||||
onChangeText: (value: string) => void;
|
||||
placeholder: string;
|
||||
inputBoxRef: React.MutableRefObject<InputBox | null>;
|
||||
multiline: boolean;
|
||||
initVal: string;
|
||||
}) => {
|
||||
const contextViewProvider = useService('contextViewService');
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
export const WidgetComponent = <CtorParams extends any[], Instance>({ ctor, propsFn, dispose, onCreateInstance }
|
||||
: {
|
||||
ctor: { new(...params: CtorParams): Instance },
|
||||
propsFn: (container: HTMLDivElement) => CtorParams,
|
||||
onCreateInstance: (instance: Instance) => IDisposable[],
|
||||
dispose: (instance: Instance) => void,
|
||||
}
|
||||
) => {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
const instance = new ctor(...propsFn(containerRef.current!));
|
||||
const disposables = onCreateInstance(instance);
|
||||
return () => {
|
||||
disposables.forEach(d => d.dispose());
|
||||
dispose(instance)
|
||||
}
|
||||
}, [ctor, propsFn, dispose, onCreateInstance, containerRef])
|
||||
|
||||
// create and mount the HistoryInputBox
|
||||
inputBoxRef.current = new InputBox(
|
||||
containerRef.current,
|
||||
return <div ref={containerRef} className='w-full' />
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, placeholder, multiline }: {
|
||||
onChangeText: (value: string) => void;
|
||||
onCreateInstance?: (instance: InputBox) => void | IDisposable[];
|
||||
inputBoxRef?: { current: InputBox | null };
|
||||
placeholder: string;
|
||||
multiline: boolean;
|
||||
}) => {
|
||||
|
||||
|
||||
const contextViewProvider = useService('contextViewService');
|
||||
|
||||
return <WidgetComponent
|
||||
ctor={InputBox}
|
||||
propsFn={useCallback((container) => [
|
||||
container,
|
||||
contextViewProvider,
|
||||
{
|
||||
inputBoxStyles: {
|
||||
|
|
@ -34,131 +61,184 @@ export const VoidInputBox = ({ onChangeText, initVal, placeholder, inputBoxRef,
|
|||
flexibleHeight: multiline,
|
||||
flexibleMaxHeight: 500,
|
||||
flexibleWidth: false,
|
||||
|
||||
}
|
||||
);
|
||||
inputBoxRef.current.value = initVal;
|
||||
|
||||
|
||||
inputBoxRef.current.onDidChange((newStr) => {
|
||||
console.log('CHANGE TEXT on inputbox', newStr)
|
||||
onChangeText(newStr)
|
||||
})
|
||||
|
||||
// cleanup
|
||||
return () => {
|
||||
if (inputBoxRef.current) {
|
||||
inputBoxRef.current.dispose();
|
||||
if (containerRef.current) {
|
||||
while (containerRef.current.firstChild) {
|
||||
containerRef.current.removeChild(containerRef.current.firstChild);
|
||||
}
|
||||
}
|
||||
inputBoxRef.current = null;
|
||||
] as const, [contextViewProvider, placeholder, multiline])}
|
||||
dispose={useCallback((instance: InputBox) => {
|
||||
instance.dispose()
|
||||
instance.element.remove()
|
||||
}, [])}
|
||||
onCreateInstance={useCallback((instance: InputBox) => {
|
||||
const disposables: IDisposable[] = []
|
||||
disposables.push(
|
||||
instance.onDidChange((newText) => onChangeText(newText))
|
||||
)
|
||||
if (onCreateInstance) {
|
||||
const ds = onCreateInstance(instance) ?? []
|
||||
disposables.push(...ds)
|
||||
}
|
||||
};
|
||||
}, [inputBoxRef, contextViewProvider, placeholder, multiline, initVal, onChangeText]);
|
||||
|
||||
return <div ref={containerRef} className="w-full" />;
|
||||
|
||||
// return <textarea
|
||||
// ref={chatInputRef}
|
||||
// placeholder={`Press ${getCmdKey()}+L to select.`}
|
||||
// onChange={(e) => { setInstructions(e.target.value) }}
|
||||
// className={`w-full p-2 leading-tight resize-none max-h-[50vh] overflow-auto bg-transparent border-none !outline-none`}
|
||||
// rows={1}
|
||||
// onInput={e => { e.currentTarget.style.height = 'auto'; e.currentTarget.style.height = e.currentTarget.scrollHeight + 'px' }} // Adjust height dynamically
|
||||
if (inputBoxRef)
|
||||
inputBoxRef.current = instance;
|
||||
|
||||
return disposables
|
||||
}, [onChangeText, onCreateInstance, inputBoxRef])
|
||||
}
|
||||
/>
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const VoidSelectBox = ({ onChangeSelection, initVal, selectBoxRef, options }: {
|
||||
onChangeSelection: (value: string) => void;
|
||||
initVal: string;
|
||||
selectBoxRef: React.MutableRefObject<SelectBox | null>;
|
||||
options: readonly string[];
|
||||
|
||||
export const VoidSelectBox = <T,>({ onChangeSelection, onCreateInstance, selectBoxRef, options }: {
|
||||
onChangeSelection: (value: T) => void;
|
||||
onCreateInstance?: ((instance: SelectBox) => void | IDisposable[]);
|
||||
selectBoxRef?: React.MutableRefObject<SelectBox | null>;
|
||||
options: readonly { text: string, value: T }[];
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const contextViewProvider = useService('contextViewService');
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
let containerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const defaultIndex = options.indexOf(initVal);
|
||||
return <WidgetComponent
|
||||
ctor={SelectBox}
|
||||
propsFn={useCallback((container) => {
|
||||
containerRef.current = container
|
||||
const defaultIndex = 0;
|
||||
return [
|
||||
options.map(opt => ({ text: opt.text })),
|
||||
defaultIndex,
|
||||
contextViewProvider,
|
||||
defaultSelectBoxStyles
|
||||
] as const;
|
||||
}, [containerRef, options, contextViewProvider])}
|
||||
|
||||
selectBoxRef.current = new SelectBox(
|
||||
options.map(opt => ({ text: opt })),
|
||||
defaultIndex,
|
||||
contextViewProvider,
|
||||
unthemedSelectBoxStyles
|
||||
);
|
||||
dispose={useCallback((instance: SelectBox) => {
|
||||
instance.dispose();
|
||||
for (let child of containerRef.current?.childNodes ?? [])
|
||||
containerRef.current?.removeChild(child)
|
||||
}, [containerRef])}
|
||||
|
||||
selectBoxRef.current.render(containerRef.current);
|
||||
onCreateInstance={useCallback((instance: SelectBox) => {
|
||||
const disposables: IDisposable[] = []
|
||||
|
||||
selectBoxRef.current.onDidSelect(e => { onChangeSelection(e.selected); });
|
||||
if (containerRef.current)
|
||||
instance.render(containerRef.current)
|
||||
|
||||
// cleanup
|
||||
return () => {
|
||||
if (selectBoxRef.current) {
|
||||
selectBoxRef.current.dispose();
|
||||
if (containerRef.current) {
|
||||
while (containerRef.current.firstChild) {
|
||||
containerRef.current.removeChild(containerRef.current.firstChild);
|
||||
}
|
||||
}
|
||||
disposables.push(
|
||||
instance.onDidSelect(e => { onChangeSelection(options[e.index].value); })
|
||||
)
|
||||
|
||||
if (onCreateInstance) {
|
||||
const ds = onCreateInstance(instance) ?? []
|
||||
disposables.push(...ds)
|
||||
}
|
||||
};
|
||||
}, [options, initVal, onChangeSelection, contextViewProvider, selectBoxRef]);
|
||||
if (selectBoxRef)
|
||||
selectBoxRef.current = instance;
|
||||
|
||||
return <div ref={containerRef} className="w-full" />;
|
||||
return disposables;
|
||||
}, [containerRef, onChangeSelection, options, onCreateInstance, selectBoxRef])}
|
||||
|
||||
/>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// export const VoidSelectBox = <T,>({ onChangeSelection, initVal, selectBoxRef, options }: {
|
||||
// initVal: T;
|
||||
// selectBoxRef: React.MutableRefObject<SelectBox | null>;
|
||||
// options: readonly { text: string, value: T }[];
|
||||
// onChangeSelection: (value: T) => void;
|
||||
// }) => {
|
||||
// const contextViewProvider = useService('contextViewService');
|
||||
// const contextMenuProvider = useService('contextMenuService');
|
||||
|
||||
|
||||
export const VoidCheckBox = ({ onChangeChecked, initVal, label, checkboxRef, }: {
|
||||
onChangeChecked: (checked: boolean) => void;
|
||||
initVal: boolean;
|
||||
checkboxRef: React.MutableRefObject<Toggle | null>;
|
||||
label: string;
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
// return <WidgetComponent
|
||||
// ctor={DropdownMenu}
|
||||
// propsFn={useCallback((container) => {
|
||||
// return [
|
||||
// container, {
|
||||
// contextMenuProvider,
|
||||
// actions: options.map(({ text, value }, i) => ({
|
||||
// id: i + '',
|
||||
// label: text,
|
||||
// tooltip: text,
|
||||
// class: undefined,
|
||||
// enabled: true,
|
||||
// run: () => {
|
||||
// onChangeSelection(value);
|
||||
// },
|
||||
// }))
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
// }] as const;
|
||||
// }, [options, initVal, contextViewProvider])}
|
||||
|
||||
// Create and mount the Checkbox using VSCode's implementation
|
||||
checkboxRef.current = new Toggle({
|
||||
title: label,
|
||||
isChecked: initVal,
|
||||
...defaultToggleStyles
|
||||
});
|
||||
// dispose={useCallback((instance: DropdownMenu) => {
|
||||
// instance.dispose();
|
||||
// // instance.element.remove()
|
||||
// }, [])}
|
||||
|
||||
containerRef.current.appendChild(checkboxRef.current.domNode);
|
||||
// onCreateInstance={useCallback((instance: DropdownMenu) => {
|
||||
// return []
|
||||
// }, [])}
|
||||
|
||||
checkboxRef.current.onChange(checked => {
|
||||
console.log('CHANGE checked state on checkbox', checked);
|
||||
onChangeChecked(checked);
|
||||
});
|
||||
|
||||
// cleanup
|
||||
return () => {
|
||||
if (checkboxRef.current) {
|
||||
checkboxRef.current.dispose();
|
||||
if (containerRef.current) {
|
||||
while (containerRef.current.firstChild) {
|
||||
containerRef.current.removeChild(containerRef.current.firstChild);
|
||||
}
|
||||
}
|
||||
checkboxRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [checkboxRef, label, initVal, onChangeChecked]);
|
||||
|
||||
return <div ref={containerRef} className="w-full" />;
|
||||
};
|
||||
// />;
|
||||
// };
|
||||
|
||||
|
||||
|
||||
|
||||
// export const VoidCheckBox = ({ onChangeChecked, initVal, label, checkboxRef, }: {
|
||||
// onChangeChecked: (checked: boolean) => void;
|
||||
// initVal: boolean;
|
||||
// checkboxRef: React.MutableRefObject<ObjectSettingCheckboxWidget | null>;
|
||||
// label: string;
|
||||
// }) => {
|
||||
// const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// const themeService = useService('themeService');
|
||||
// const contextViewService = useService('contextViewService');
|
||||
// const hoverService = useService('hoverService');
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!containerRef.current) return;
|
||||
|
||||
// // Create and mount the Checkbox using VSCode's implementation
|
||||
|
||||
// checkboxRef.current = new ObjectSettingCheckboxWidget(
|
||||
// containerRef.current,
|
||||
// themeService,
|
||||
// contextViewService,
|
||||
// hoverService,
|
||||
// );
|
||||
|
||||
|
||||
// checkboxRef.current.setValue([{
|
||||
// key: { type: 'string', data: label },
|
||||
// value: { type: 'boolean', data: initVal },
|
||||
// removable: false,
|
||||
// resetable: true,
|
||||
// }])
|
||||
|
||||
// checkboxRef.current.onDidChangeList((list) => {
|
||||
// onChangeChecked(!!list);
|
||||
// })
|
||||
|
||||
|
||||
// // cleanup
|
||||
// return () => {
|
||||
// if (checkboxRef.current) {
|
||||
// checkboxRef.current.dispose();
|
||||
// if (containerRef.current) {
|
||||
// while (containerRef.current.firstChild) {
|
||||
// containerRef.current.removeChild(containerRef.current.firstChild);
|
||||
// }
|
||||
// }
|
||||
// checkboxRef.current = null;
|
||||
// }
|
||||
// };
|
||||
// }, [checkboxRef, label, initVal, onChangeChecked]);
|
||||
|
||||
// return <div ref={containerRef} className="w-full" />;
|
||||
// };
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
|
|
|||
|
|
@ -1,161 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
import { AlertCircle, ChevronDown, ChevronUp, X } from 'lucide-react';
|
||||
|
||||
import { getCmdKey } from '../../../getCmdKey.js';
|
||||
|
||||
// const opaqueMessage = `\
|
||||
// Unfortunately, Void can't see the full error. However, you should be able to find more details by pressing ${getCmdKey()}+Shift+P, typing "Toggle Developer Tools", and looking at the console.\n
|
||||
// This error often means you have an incorrect API key. If you're self-hosting your own server, it might mean your CORS headers are off, and you should make sure your server's response has the header "Access-Control-Allow-Origins" set to "*", or at least allows "vscode-file://vscode-app".`
|
||||
// if ((error instanceof Error) && (error.cause + '').includes('TypeError: Failed to fetch')) {
|
||||
// e = error as any
|
||||
// e['Void Team'] = opaqueMessage
|
||||
// }
|
||||
|
||||
|
||||
type Details = {
|
||||
message: string,
|
||||
name: string,
|
||||
stack: string | null,
|
||||
cause: string | null,
|
||||
code: string | null,
|
||||
additional: Record<string, any>
|
||||
}
|
||||
|
||||
// Get detailed error information
|
||||
const getErrorDetails = (error: unknown) => {
|
||||
|
||||
let details: Details;
|
||||
|
||||
let e: Error & { [other: string]: undefined | any }
|
||||
|
||||
// If fetch() fails, it gives an opaque message. We add extra details to the error.
|
||||
if (error instanceof Error) {
|
||||
e = error
|
||||
}
|
||||
// sometimes error is an object but not an Error
|
||||
else if (typeof error === 'object') {
|
||||
e = new Error(`The server didn't give a very useful error message. More details below.`, { cause: JSON.stringify(error) })
|
||||
|
||||
}
|
||||
else {
|
||||
e = new Error(String(error))
|
||||
}
|
||||
// console.log('error display', JSON.stringify(e))
|
||||
|
||||
const message = e.message && e.error ?
|
||||
(e.message + ':\n' + e.error)
|
||||
: e.message || e.error || JSON.stringify(error)
|
||||
|
||||
details = {
|
||||
name: e.name || 'Error',
|
||||
message: message,
|
||||
stack: null, // e.stack is ignored because it's ugly and not very useful
|
||||
cause: e.cause ? String(e.cause) : null,
|
||||
code: e.code || null,
|
||||
additional: {}
|
||||
}
|
||||
|
||||
|
||||
// Collect any additional properties from the e
|
||||
for (let prop of Object.getOwnPropertyNames(e).filter((prop) => !Object.keys(details).includes(prop)))
|
||||
details.additional[prop] = (e as any)[prop]
|
||||
|
||||
return details;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const ErrorDisplay = ({
|
||||
error,
|
||||
onDismiss = null,
|
||||
showDismiss = true,
|
||||
className = ''
|
||||
}: {
|
||||
error: Error | object | string,
|
||||
onDismiss: (() => void) | null,
|
||||
showDismiss?: boolean,
|
||||
className?: string
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const details = getErrorDetails(error);
|
||||
const hasDetails = details.cause || Object.keys(details.additional).length > 0;
|
||||
|
||||
return (
|
||||
<div className={`rounded-lg border border-red-200 bg-red-50 p-4 ${className}`}>
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex gap-3">
|
||||
<AlertCircle className="h-5 w-5 text-red-500 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-red-800">
|
||||
{details.name}
|
||||
</h3>
|
||||
<p className="text-red-700 mt-1">
|
||||
{details.message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
{hasDetails && (
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="text-red-600 hover:text-red-800 p-1 rounded"
|
||||
>
|
||||
{isExpanded ? (
|
||||
<ChevronUp className="h-5 w-5" />
|
||||
) : (
|
||||
<ChevronDown className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
{showDismiss && onDismiss && (
|
||||
<button
|
||||
onClick={onDismiss}
|
||||
className="text-red-600 hover:text-red-800 p-1 rounded"
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Expandable Details */}
|
||||
{isExpanded && hasDetails && (
|
||||
<div className="mt-4 space-y-3 border-t border-red-200 pt-3">
|
||||
{details.code && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Error Code: </span>
|
||||
<span className="text-red-700">{details.code}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{details.cause && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Cause: </span>
|
||||
<span className="text-red-700">{details.cause}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{Object.keys(details.additional).length > 0 && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Additional Information:</span>
|
||||
<pre className="mt-1 text-sm text-red-700 overflow-x-auto whitespace-pre-wrap">
|
||||
{Object.keys(details.additional).map(key => `${key}:\n${details.additional[key]}`).join('\n')}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
{/* {details.stack && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Stack Trace:</span>
|
||||
<pre className="mt-1 text-sm text-red-700 overflow-x-auto whitespace-pre-wrap">
|
||||
{details.stack}
|
||||
</pre>
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { diffLines, Change } from 'diff';
|
||||
|
||||
export { diffLines, Change }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import * as ReactDOM from 'react-dom/client'
|
||||
import { ReactServicesType, VoidSidebarState } from '../../../registerSidebar.js';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { VoidSidebarState, ReactServicesType } from '../../../registerSidebar.js'
|
||||
import { ThreadsState } from '../../../registerThreads.js'
|
||||
import { VoidProviderState } from '../../../../../../../platform/void/common/configTypes.js'
|
||||
import { SettingsOfProvider } from '../../../../../../../platform/void/common/voidConfigTypes.js'
|
||||
|
||||
|
||||
// normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes
|
||||
|
|
@ -10,13 +15,13 @@ let services: ReactServicesType
|
|||
|
||||
// even if React hasn't mounted yet, these variables are always updated to the latest state:
|
||||
let sidebarState: VoidSidebarState
|
||||
let configState: VoidProviderState
|
||||
let threadsState: ThreadsState
|
||||
let settingsOfProvider: SettingsOfProvider
|
||||
|
||||
// React listens by adding a setState function to these:
|
||||
const sidebarStateListeners: Set<(s: VoidSidebarState) => void> = new Set()
|
||||
const configStateListeners: Set<(s: VoidProviderState) => void> = new Set()
|
||||
const threadsStateListeners: Set<(s: ThreadsState) => void> = new Set()
|
||||
const settingsOfProviderListeners: Set<(s: SettingsOfProvider) => void> = new Set()
|
||||
|
||||
// must call this before you can use any of the hooks below
|
||||
// this should only be called ONCE! this is the only place you don't need to dispose onDidChange. If you use state.onDidChange anywhere else, make sure to dispose it!
|
||||
|
|
@ -25,7 +30,7 @@ let wasCalled = false
|
|||
|
||||
export const _registerServices = (services_: ReactServicesType) => {
|
||||
|
||||
if (wasCalled) console.error(`void _registerServices was called again! It should only be called once.`)
|
||||
if (wasCalled) console.error(`⚠️ Void _registerServices was called again! It should only be called once.`)
|
||||
wasCalled = true
|
||||
|
||||
services = services_
|
||||
|
|
@ -37,11 +42,6 @@ export const _registerServices = (services_: ReactServicesType) => {
|
|||
sidebarStateListeners.forEach(l => l(sidebarState))
|
||||
})
|
||||
|
||||
configState = configStateService.state
|
||||
configStateService.onDidChangeState(() => {
|
||||
configState = configStateService.state
|
||||
configStateListeners.forEach(l => l(configState))
|
||||
})
|
||||
|
||||
threadsState = threadsStateService.state
|
||||
threadsStateService.onDidChangeCurrentThread(() => {
|
||||
|
|
@ -49,15 +49,20 @@ export const _registerServices = (services_: ReactServicesType) => {
|
|||
threadsStateListeners.forEach(l => l(threadsState))
|
||||
})
|
||||
|
||||
settingsOfProvider = configStateService.state.settingsOfProvider
|
||||
configStateService.onDidChangeState(() => {
|
||||
settingsOfProvider = configStateService.state.settingsOfProvider
|
||||
settingsOfProviderListeners.forEach(l => l(settingsOfProvider))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// -- services --
|
||||
export const useService = <T extends keyof ReactServicesType,>(serviceName: T) => {
|
||||
export const useService = <T extends keyof ReactServicesType,>(serviceName: T): ReactServicesType[T] => {
|
||||
if (services === null) {
|
||||
throw new Error('useAccessor must be used within an AccessorProvider')
|
||||
}
|
||||
return services[serviceName] as ReactServicesType[T]
|
||||
return services[serviceName]
|
||||
}
|
||||
|
||||
// -- state of services --
|
||||
|
|
@ -73,11 +78,11 @@ export const useSidebarState = () => {
|
|||
}
|
||||
|
||||
export const useConfigState = () => {
|
||||
const [s, ss] = useState(configState)
|
||||
const [s, ss] = useState(settingsOfProvider)
|
||||
useEffect(() => {
|
||||
ss(configState)
|
||||
configStateListeners.add(ss)
|
||||
return () => { configStateListeners.delete(ss) }
|
||||
ss(settingsOfProvider)
|
||||
settingsOfProviderListeners.add(ss)
|
||||
return () => { settingsOfProviderListeners.delete(ss) }
|
||||
}, [ss])
|
||||
return s
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src2/**/*.{jsx,tsx}'], // uses these files to decide how to transform the css file
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"exactOptionalPropertyTypes": false,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { defineConfig } from 'tsup'
|
||||
|
||||
export default defineConfig({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
|
||||
|
|
@ -12,7 +12,6 @@ import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js
|
|||
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
|
||||
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
|
||||
import { CodeStagingSelection, IThreadHistoryService } from './registerThreads.js';
|
||||
// import { IVoidConfigService } from './registerSettings.js';
|
||||
// import { IEditorService } from '../../../services/editor/common/editorService.js';
|
||||
|
||||
import { IEditorService } from '../../../services/editor/common/editorService.js';
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js';
|
||||
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IVoidConfigStateService } from './registerConfig.js';
|
||||
import { ITextModel } from '../../../../editor/common/model.js';
|
||||
import { Position } from '../../../../editor/common/core/position.js';
|
||||
import { InlineCompletion, InlineCompletionContext } from '../../../../editor/common/languages.js';
|
||||
|
|
@ -516,7 +515,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
const disabled = true
|
||||
const testMode = false
|
||||
|
||||
if (disabled) { return []; }
|
||||
if (disabled) return [];
|
||||
|
||||
const docUriStr = model.uri.toString();
|
||||
|
||||
|
|
@ -671,13 +670,13 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
resolve(newAutocompletion.insertText)
|
||||
|
||||
},
|
||||
onError: ({ error }) => {
|
||||
onError: ({ message }) => {
|
||||
newAutocompletion.endTime = Date.now()
|
||||
newAutocompletion.status = 'error'
|
||||
reject(error)
|
||||
reject(message)
|
||||
},
|
||||
providerName: 'anthropic',
|
||||
voidConfig: this._voidConfigStateService.state,
|
||||
featureName: 'Autocomplete',
|
||||
range: { startLineNumber: position.lineNumber, startColumn: position.column, endLineNumber: position.lineNumber, endColumn: position.column },
|
||||
})
|
||||
newAutocompletion.requestId = requestId
|
||||
|
||||
|
|
@ -714,7 +713,6 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
|
||||
constructor(
|
||||
@ILanguageFeaturesService private _langFeatureService: ILanguageFeaturesService,
|
||||
@IVoidConfigStateService private readonly _voidConfigStateService: IVoidConfigStateService,
|
||||
@ISendLLMMessageService private readonly _sendLLMMessageService: ISendLLMMessageService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
|
|
|
|||
|
|
@ -1,106 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from '../../../../base/common/event.js';
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { deepClone } from '../../../../base/common/objects.js';
|
||||
import { IEncryptionService } from '../../../../platform/encryption/common/encryptionService.js';
|
||||
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
|
||||
import { defaultVoidProviderState, ProviderName, VoidProviderState } from '../../../../platform/void/common/configTypes.js';
|
||||
|
||||
|
||||
|
||||
const VOID_CONFIG_KEY = 'void.config'
|
||||
|
||||
type SetStateFn = <K extends ProviderName>(
|
||||
providerName: K,
|
||||
option: keyof VoidProviderState[K],
|
||||
newVal: string
|
||||
) => Promise<void>;
|
||||
|
||||
|
||||
export interface IVoidConfigStateService {
|
||||
readonly _serviceBrand: undefined;
|
||||
readonly state: VoidProviderState;
|
||||
onDidChangeState: Event<void>;
|
||||
setState: SetStateFn;
|
||||
}
|
||||
|
||||
export const IVoidConfigStateService = createDecorator<IVoidConfigStateService>('VoidConfigStateService');
|
||||
class VoidConfigStateService extends Disposable implements IVoidConfigStateService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes
|
||||
|
||||
state: VoidProviderState;
|
||||
|
||||
// readonly voidConfigInfo: VoidConfigInfo = voidConfigInfo; // just putting this here for simplicity, it's static though
|
||||
get _defaultState() {
|
||||
return deepClone(defaultVoidProviderState)
|
||||
}
|
||||
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IEncryptionService private readonly _encryptionService: IEncryptionService,
|
||||
// could have used this, but it's clearer the way it is (+ slightly different eg StorageTarget.USER)
|
||||
// @ISecretStorageService private readonly _secretStorageService: ISecretStorageService,
|
||||
) {
|
||||
super()
|
||||
|
||||
// at the start, we haven't read the partial config yet, but we need to set state to something, just treat partialVoidConfig like it's empty
|
||||
this.state = this._defaultState
|
||||
|
||||
// read and update the actual state immediately
|
||||
this._readVoidConfigState().then(voidConfigState => {
|
||||
this._setState(voidConfigState)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async _readVoidConfigState(): Promise<VoidProviderState> {
|
||||
const encryptedPartialConfig = this._storageService.get(VOID_CONFIG_KEY, StorageScope.APPLICATION)
|
||||
|
||||
if (!encryptedPartialConfig)
|
||||
return this._defaultState
|
||||
|
||||
const voidConfigStateStr = await this._encryptionService.decrypt(encryptedPartialConfig)
|
||||
return JSON.parse(voidConfigStateStr)
|
||||
}
|
||||
|
||||
|
||||
private async _storeVoidConfigState(voidConfigState: VoidProviderState) {
|
||||
const encryptedVoidConfigStr = await this._encryptionService.encrypt(JSON.stringify(voidConfigState))
|
||||
this._storageService.store(VOID_CONFIG_KEY, encryptedVoidConfigStr, StorageScope.APPLICATION, StorageTarget.USER)
|
||||
}
|
||||
|
||||
|
||||
// Set field on PartialVoidConfig
|
||||
setState: SetStateFn = async (providerName, option, newVal) => {
|
||||
const newState: VoidProviderState = {
|
||||
...this.state,
|
||||
[providerName]: {
|
||||
...this.state[providerName],
|
||||
[option]: newVal,
|
||||
}
|
||||
}
|
||||
await this._storeVoidConfigState(newState)
|
||||
this._setState(newState)
|
||||
}
|
||||
|
||||
// internal function to update state, should be called every time state changes
|
||||
private async _setState(voidConfigState: VoidProviderState) {
|
||||
this.state = voidConfigState
|
||||
this._onDidChangeState.fire()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(IVoidConfigStateService, VoidConfigStateService, InstantiationType.Eager);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
|
|
@ -11,7 +11,7 @@ import { ICodeEditor, IOverlayWidget, IViewZone } from '../../../../editor/brows
|
|||
// import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js';
|
||||
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
|
||||
// import { throttle } from '../../../../base/common/decorators.js';
|
||||
import { IVoidConfigStateService } from './registerConfig.js';
|
||||
// import { IVoidConfigStateService } from './registerConfig.js';
|
||||
import { writeFileWithDiffInstructions } from './prompt/systemPrompts.js';
|
||||
import { ComputedDiff, findDiffs } from './findDiffs.js';
|
||||
import { EndOfLinePreference, ITextModel } from '../../../../editor/common/model.js';
|
||||
|
|
@ -28,9 +28,8 @@ import { ILanguageService } from '../../../../editor/common/languages/language.j
|
|||
import * as dom from '../../../../base/browser/dom.js';
|
||||
import { Widget } from '../../../../base/browser/ui/widget.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { LLMMessageServiceParams } from '../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { LLMFeatureSelection, LLMMessageServiceParams } from '../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { ISendLLMMessageService } from '../../../../platform/void/browser/llmMessageService.js';
|
||||
import { ProviderName } from '../../../../platform/void/common/configTypes.js';
|
||||
|
||||
|
||||
// gets converted to --vscode-void-greenBG, see void.css
|
||||
|
|
@ -104,25 +103,17 @@ type HistorySnapshot = {
|
|||
entireFileCode: string;
|
||||
} &
|
||||
({
|
||||
type: 'ctrl+k';
|
||||
type: 'Ctrl+K';
|
||||
ctrlKText: string;
|
||||
} | {
|
||||
type: 'ctrl+l';
|
||||
type: 'Ctrl+L';
|
||||
})
|
||||
|
||||
|
||||
type StartStreamingOptions = {
|
||||
type: 'ctrl+k',
|
||||
providerName: ProviderName,
|
||||
range: IRange
|
||||
} | {
|
||||
type: 'ctrl+l',
|
||||
providerName: ProviderName
|
||||
}
|
||||
|
||||
export interface IInlineDiffsService {
|
||||
readonly _serviceBrand: undefined;
|
||||
startStreaming(params: StartStreamingOptions, str: string): void;
|
||||
startStreaming(params: LLMFeatureSelection, str: string): void;
|
||||
}
|
||||
|
||||
export const IInlineDiffsService = createDecorator<IInlineDiffsService>('inlineDiffAreasService');
|
||||
|
|
@ -153,7 +144,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
|
||||
constructor(
|
||||
// @IHistoryService private readonly _historyService: IHistoryService, // history service is the history of pressing alt left/right
|
||||
@IVoidConfigStateService private readonly _voidConfigStateService: IVoidConfigStateService,
|
||||
// @IVoidConfigStateService private readonly _voidConfigStateService: IVoidConfigStateService,
|
||||
@ICodeEditorService private readonly _editorService: ICodeEditorService,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService, // undoRedo service is the history of pressing ctrl+z
|
||||
|
|
@ -379,7 +370,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
return {
|
||||
snapshottedDiffAreaOfId,
|
||||
entireFileCode: this._readURI(uri) ?? '', // the whole file's code
|
||||
type: 'ctrl+l',
|
||||
type: 'Ctrl+L',
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -646,7 +637,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
|
||||
|
||||
|
||||
private async _initializeStream(uri: URI, diffRepr: string, providerName: ProviderName) {
|
||||
private async _initializeStream(opts: LLMFeatureSelection, diffRepr: string, uri: URI,) {
|
||||
|
||||
// diff area begin and end line
|
||||
const numLines = this._getNumLines(uri)
|
||||
|
|
@ -698,7 +689,6 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
this.diffAreaOfId[diffArea.diffareaid] = diffArea
|
||||
|
||||
// actually call the LLM
|
||||
const voidConfigState = this._voidConfigStateService.state
|
||||
const promptContent = `\
|
||||
ORIGINAL_CODE
|
||||
\`\`\`
|
||||
|
|
@ -715,29 +705,6 @@ Please finish writing the new file by applying the diff to the original file. Re
|
|||
`
|
||||
|
||||
|
||||
// CTRL+K prompt:
|
||||
// const promptContent = `Here is the user's original selection:
|
||||
// \`\`\`
|
||||
// <MID>${selection}</MID>
|
||||
// \`\`\`
|
||||
|
||||
// The user wants to apply the following instructions to the selection:
|
||||
// ${instructions}
|
||||
|
||||
// Please rewrite the selection following the user's instructions.
|
||||
|
||||
// Instructions to follow:
|
||||
// 1. Follow the user's instructions
|
||||
// 2. You may ONLY CHANGE the selection, and nothing else in the file
|
||||
// 3. Make sure all brackets in the new selection are balanced the same was as in the original selection
|
||||
// 3. Be careful not to duplicate or remove variables, comments, or other syntax by mistake
|
||||
|
||||
// Complete the following:
|
||||
// \`\`\`
|
||||
// <PRE>${prefix}</PRE>
|
||||
// <SUF>${suffix}</SUF>
|
||||
// <MID>`;
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
|
||||
let streamRequestId: string | null = null
|
||||
|
|
@ -770,8 +737,7 @@ Please finish writing the new file by applying the diff to the original file. Re
|
|||
diffArea._sweepState = { isStreaming: false, line: null }
|
||||
resolve();
|
||||
},
|
||||
voidConfig: voidConfigState,
|
||||
providerName,
|
||||
...opts
|
||||
}
|
||||
|
||||
streamRequestId = this._sendLLMMessageService.sendLLMMessage(object)
|
||||
|
|
@ -786,7 +752,7 @@ Please finish writing the new file by applying the diff to the original file. Re
|
|||
|
||||
|
||||
|
||||
async startStreaming(params: StartStreamingOptions, userMessage: string) {
|
||||
async startStreaming(opts: LLMFeatureSelection, userMessage: string) {
|
||||
|
||||
const editor = this._editorService.getActiveCodeEditor()
|
||||
if (!editor) return
|
||||
|
|
@ -798,7 +764,7 @@ Please finish writing the new file by applying the diff to the original file. Re
|
|||
|
||||
// TODO deselect user's cursor
|
||||
|
||||
this._initializeStream(uri, userMessage, params.providerName)
|
||||
this._initializeStream(opts, userMessage, uri)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Registry } from '../../../../platform/registry/common/platform.js';
|
||||
|
|
@ -42,7 +42,7 @@ import { IHoverService } from '../../../../platform/hover/browser/hover.js';
|
|||
|
||||
import mountFn from './react/out/sidebar-tsx/Sidebar.js';
|
||||
|
||||
import { IVoidConfigStateService } from './registerConfig.js';
|
||||
import { IVoidConfigStateService } from '../../../../platform/void/common/voidConfigService.js';
|
||||
import { IFileService } from '../../../../platform/files/common/files.js';
|
||||
import { IInlineDiffsService } from './registerInlineDiffs.js';
|
||||
import { IModelService } from '../../../../editor/common/services/model.js';
|
||||
|
|
@ -70,6 +70,9 @@ export type ReactServicesType = {
|
|||
sendLLMMessageService: ISendLLMMessageService;
|
||||
clipboardService: IClipboardService;
|
||||
|
||||
themeService: IThemeService,
|
||||
hoverService: IHoverService,
|
||||
|
||||
contextViewService: IContextViewService;
|
||||
contextMenuService: IContextMenuService;
|
||||
}
|
||||
|
|
@ -113,6 +116,8 @@ class VoidSidebarViewPane extends ViewPane {
|
|||
inlineDiffService: accessor.get(IInlineDiffsService),
|
||||
sendLLMMessageService: accessor.get(ISendLLMMessageService),
|
||||
clipboardService: accessor.get(IClipboardService),
|
||||
themeService: accessor.get(IThemeService),
|
||||
hoverService: accessor.get(IHoverService),
|
||||
contextViewService: accessor.get(IContextViewService),
|
||||
contextMenuService: accessor.get(IContextMenuService),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// register keybinds
|
||||
import './registerActions.js'
|
||||
|
||||
// register Settings
|
||||
import './registerConfig.js' // TODO move this to platform
|
||||
|
||||
// register inline diffs
|
||||
import './registerInlineDiffs.js'
|
||||
|
||||
|
|
|
|||
|
|
@ -817,7 +817,7 @@ export class GettingStartedPage extends EditorPane {
|
|||
|
||||
const header = $('.header', {},
|
||||
$('h1.product-name.caption', {}, this.productService.nameLong),
|
||||
$('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved"))
|
||||
$('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "The open source AI code editor."))
|
||||
);
|
||||
|
||||
const leftColumn = $('.categories-column.categories-column-left', {},);
|
||||
|
|
|
|||
|
|
@ -248,30 +248,30 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
|
|||
type: 'svg', altText: 'Language extensions', path: 'languages.svg'
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
title: localize('gettingStarted.settings.title', "Tune your settings"),
|
||||
description: localize('gettingStarted.settings.description.interpolated', "Customize every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n{0}", Button(localize('tweakSettings', "Open Settings"), 'command:toSide:workbench.action.openSettings')),
|
||||
media: {
|
||||
type: 'svg', altText: 'VS Code Settings', path: 'settings.svg'
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'settingsSync',
|
||||
title: localize('gettingStarted.settingsSync.title', "Sync settings across devices"),
|
||||
description: localize('gettingStarted.settingsSync.description.interpolated', "Keep your essential customizations backed up and updated across all your devices.\n{0}", Button(localize('enableSync', "Backup and Sync Settings"), 'command:workbench.userDataSync.actions.turnOn')),
|
||||
when: 'syncStatus != uninitialized',
|
||||
completionEvents: ['onEvent:sync-enabled'],
|
||||
media: {
|
||||
type: 'svg', altText: 'The "Turn on Sync" entry in the settings gear menu.', path: 'settingsSync.svg'
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'commandPaletteTask',
|
||||
title: localize('gettingStarted.commandPalette.title', "Unlock productivity with the Command Palette "),
|
||||
description: localize('gettingStarted.commandPalette.description.interpolated', "Run commands without reaching for your mouse to accomplish any task in VS Code.\n{0}", Button(localize('commandPalette', "Open Command Palette"), 'command:workbench.action.showCommands')),
|
||||
media: { type: 'svg', altText: 'Command Palette overlay for searching and executing commands.', path: 'commandPalette.svg' },
|
||||
},
|
||||
// {
|
||||
// id: 'settings',
|
||||
// title: localize('gettingStarted.settings.title', "Tune your settings"),
|
||||
// description: localize('gettingStarted.settings.description.interpolated', "Customize every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n{0}", Button(localize('tweakSettings', "Open Settings"), 'command:toSide:workbench.action.openSettings')),
|
||||
// media: {
|
||||
// type: 'svg', altText: 'VS Code Settings', path: 'settings.svg'
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// id: 'settingsSync',
|
||||
// title: localize('gettingStarted.settingsSync.title', "Sync settings across devices"),
|
||||
// description: localize('gettingStarted.settingsSync.description.interpolated', "Keep your essential customizations backed up and updated across all your devices.\n{0}", Button(localize('enableSync', "Backup and Sync Settings"), 'command:workbench.userDataSync.actions.turnOn')),
|
||||
// when: 'syncStatus != uninitialized',
|
||||
// completionEvents: ['onEvent:sync-enabled'],
|
||||
// media: {
|
||||
// type: 'svg', altText: 'The "Turn on Sync" entry in the settings gear menu.', path: 'settingsSync.svg'
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// id: 'commandPaletteTask',
|
||||
// title: localize('gettingStarted.commandPalette.title', "Unlock productivity with the Command Palette "),
|
||||
// description: localize('gettingStarted.commandPalette.description.interpolated', "Run commands without reaching for your mouse to accomplish any task in VS Code.\n{0}", Button(localize('commandPalette', "Open Command Palette"), 'command:workbench.action.showCommands')),
|
||||
// media: { type: 'svg', altText: 'Command Palette overlay for searching and executing commands.', path: 'commandPalette.svg' },
|
||||
// },
|
||||
{
|
||||
id: 'pickAFolderTask-Mac',
|
||||
title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"),
|
||||
|
|
@ -299,12 +299,12 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
|
|||
type: 'svg', altText: 'Go to file in quick search.', path: 'search.svg'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'videoTutorial',
|
||||
title: localize('gettingStarted.videoTutorial.title', "Watch video tutorials"),
|
||||
description: localize('gettingStarted.videoTutorial.description.interpolated', "Watch the first in a series of short & practical video tutorials for VS Code's key features.\n{0}", Button(localize('watch', "Watch Tutorial"), 'https://aka.ms/vscode-getting-started-video')),
|
||||
media: { type: 'svg', altText: 'VS Code Settings', path: 'learn.svg' },
|
||||
}
|
||||
// {
|
||||
// id: 'videoTutorial',
|
||||
// title: localize('gettingStarted.videoTutorial.title', "Watch video tutorials"),
|
||||
// description: localize('gettingStarted.videoTutorial.description.interpolated', "Watch the first in a series of short & practical video tutorials for VS Code's key features.\n{0}", Button(localize('watch', "Watch Tutorial"), 'https://aka.ms/vscode-getting-started-video')),
|
||||
// media: { type: 'svg', altText: 'VS Code Settings', path: 'learn.svg' },
|
||||
// }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import './browser/workbench.contribution.js';
|
|||
// Void added this:
|
||||
import './contrib/void/browser/void.contribution.js';
|
||||
import '../platform/void/browser/llmMessageService.js';
|
||||
import '../platform/void/common/voidConfigService.js';
|
||||
//#endregion
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue