From f7489375b0ff6df3c5ac140c3272f6531d53a0f7 Mon Sep 17 00:00:00 2001 From: Andrew Pareles <43356051+andrewpareles@users.noreply.github.com> Date: Fri, 9 May 2025 17:03:16 -0700 Subject: [PATCH 01/30] Update issue_template.md --- .github/ISSUE_TEMPLATE/issue_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/issue_template.md b/.github/ISSUE_TEMPLATE/issue_template.md index 939b9c1d..fe6c4301 100644 --- a/.github/ISSUE_TEMPLATE/issue_template.md +++ b/.github/ISSUE_TEMPLATE/issue_template.md @@ -6,4 +6,4 @@ title: For VSCode-related issues (eg builds), please start the title with `[App] 1. Press `Cmd+Shift+P` in Void, and type `Help: About`. Please paste the information here. Also let us know any other relevant details, like the model and provider you're using if applicable. -2. Describe the issue/feature here. +2. Describe the issue/feature here! From 15cf3835241126e6e78fc655dcba59b6a6d93961 Mon Sep 17 00:00:00 2001 From: Andrew Pareles <43356051+andrewpareles@users.noreply.github.com> Date: Fri, 9 May 2025 17:13:15 -0700 Subject: [PATCH 02/30] Update README.md --- README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7b102e3a..0dfd3e10 100644 --- a/README.md +++ b/README.md @@ -15,26 +15,23 @@ Use AI agents on your codebase, checkpoint and visualize changes, and bring any This repo contains the full sourcecode for Void. If you're new, welcome! +- 🧭 [Website](https://voideditor.com) + - 👋 [Discord](https://discord.gg/RSNjgaugJs) -- 🚙 [Roadmap](https://github.com/orgs/voideditor/projects/2) +- 🚙 [Project Board](https://github.com/orgs/voideditor/projects/2) -- 📝 [Changelog](https://voideditor.com/changelog) - -- 🧭 [Website](https://voideditor.com) ## Contributing -1. To get started working on Void, see [`HOW_TO_CONTRIBUTE`](https://github.com/voideditor/void/blob/main/HOW_TO_CONTRIBUTE.md). +1. To get started working on Void, check out our Project Board! You can also see [HOW_TO_CONTRIBUTE](https://github.com/voideditor/void/blob/main/HOW_TO_CONTRIBUTE.md). -2. Feel free to attend a weekly meeting in our Discord channel! - -3. We're open to collaborations and suggestions of all types - just reach out. +2. Feel free to attend a casual weekly meeting in our Discord channel! ## Reference -[Void](https://voideditor.com) is a fork of the [vscode](https://github.com/microsoft/vscode) repository. For a guide to the codebase, see [`VOID_CODEBASE_GUIDE`](https://github.com/voideditor/void/blob/main/VOID_CODEBASE_GUIDE.md). +[Void](https://voideditor.com) is a fork of the [vscode](https://github.com/microsoft/vscode) repository. For a guide to the codebase, see [VOID_CODEBASE_GUIDE](https://github.com/voideditor/void/blob/main/VOID_CODEBASE_GUIDE.md). ## Support You can always reach us in our Discord server or contact us via email: hello@voideditor.com. From f33cf14b077d3b57f87b20ff9133377f0e07dfde Mon Sep 17 00:00:00 2001 From: Andrew Pareles <43356051+andrewpareles@users.noreply.github.com> Date: Fri, 9 May 2025 17:13:35 -0700 Subject: [PATCH 03/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0dfd3e10..78278598 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ This repo contains the full sourcecode for Void. If you're new, welcome! ## Reference -[Void](https://voideditor.com) is a fork of the [vscode](https://github.com/microsoft/vscode) repository. For a guide to the codebase, see [VOID_CODEBASE_GUIDE](https://github.com/voideditor/void/blob/main/VOID_CODEBASE_GUIDE.md). +Void is a fork of the [vscode](https://github.com/microsoft/vscode) repository. For a guide to the codebase, see [VOID_CODEBASE_GUIDE](https://github.com/voideditor/void/blob/main/VOID_CODEBASE_GUIDE.md). ## Support You can always reach us in our Discord server or contact us via email: hello@voideditor.com. From 93b727198e6ece7def819c016d62f95300be4074 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 02:09:49 -0700 Subject: [PATCH 04/30] fix linux update message --- .../contrib/void/electron-main/voidUpdateMainService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/void/electron-main/voidUpdateMainService.ts b/src/vs/workbench/contrib/void/electron-main/voidUpdateMainService.ts index ab471631..da7a6956 100644 --- a/src/vs/workbench/contrib/void/electron-main/voidUpdateMainService.ts +++ b/src/vs/workbench/contrib/void/electron-main/voidUpdateMainService.ts @@ -94,7 +94,7 @@ export class VoidMainUpdateService extends Disposable implements IVoidUpdateServ const data = await response.json(); const version = data.tag_name; - const myVersion = `${this._productService.version}.${this._productService.release}` + const myVersion = this._productService.version const latestVersion = version const isUpToDate = myVersion === latestVersion // only makes sense if response.ok From 85b1f0ec04108c161c9538fa915118cb5d36ff96 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 03:20:03 -0700 Subject: [PATCH 05/30] apply button failure better UX --- .../browser/react/src/markdown/ApplyBlockHoverButtons.tsx | 7 ++++++- src/vs/workbench/contrib/void/common/prompt/prompts.ts | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx index 41b5a649..72d298c2 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx @@ -253,6 +253,7 @@ export const ApplyButtonsHTML = ({ const accessor = useAccessor() const editCodeService = accessor.get('IEditCodeService') const metricsService = accessor.get('IMetricsService') + const notificationService = accessor.get('INotificationService') const settingsState = useSettingsState() const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || !applyBoxId @@ -271,13 +272,17 @@ export const ApplyButtonsHTML = ({ uri: uri, startBehavior: 'reject-conflicts', }) ?? [] - console.log('setting!!!', newApplyingUri) setApplying(newApplyingUri) + if (!applyDonePromise) { + notificationService.info(`Void Error: We couldn't run Apply here (${uri === 'current' ? 'this Apply block wants to run on the current file, but you might not have a file open' : uri.fsPath}).`) + } + // catch any errors by interrupting the stream applyDonePromise?.catch(e => { const uri = getUriBeingApplied(applyBoxId) if (uri) editCodeService.interruptURIStreaming({ uri: uri }) + notificationService.info(`Void Error: There was a problem running Apply: ${e}.`) }) metricsService.capture('Apply Code', { length: codeStr.length }) // capture the length only diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index 613142b8..ac39ee29 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -480,11 +480,13 @@ ${directoryStr} } - if (mode === 'gather' || mode === 'normal') { - details.push(`If you write any code blocks, please use this format: + details.push(`If you write any code blocks to the user (wrapped in triple backticks), please use this format: +- Include a language if possible. Terminal should have the language 'shell'. - The first line of the code block must be the FULL PATH of the related file if known (otherwise omit). - The remaining contents of the file should proceed as usual.`) + if (mode === 'gather' || mode === 'normal') { + details.push(`If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion in CODE BLOCK(S). - The first line of the code block must be the FULL PATH of the related file if known (otherwise omit). - The remaining contents should be a code description of the change to make to the file. \ From 58d95035ff14009bc0ae35bae0c749580566bfb0 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 03:28:29 -0700 Subject: [PATCH 06/30] update message --- .../void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx index 72d298c2..56c460ce 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx @@ -275,7 +275,7 @@ export const ApplyButtonsHTML = ({ setApplying(newApplyingUri) if (!applyDonePromise) { - notificationService.info(`Void Error: We couldn't run Apply here (${uri === 'current' ? 'this Apply block wants to run on the current file, but you might not have a file open' : uri.fsPath}).`) + notificationService.info(`Void Error: We couldn't run Apply here. ${uri === 'current' ? 'This Apply block wants to run on the current file, but you might not have a file open.' : `This Apply block wants to run on ${uri.fsPath}, but it might not exist`}.`) } // catch any errors by interrupting the stream From e7436575a5c0c6fb62ce4f4b916f27bd29979648 Mon Sep 17 00:00:00 2001 From: Andrew Pareles <43356051+andrewpareles@users.noreply.github.com> Date: Sat, 10 May 2025 03:28:39 -0700 Subject: [PATCH 07/30] Revert "apply button failure better UX" --- .../browser/react/src/markdown/ApplyBlockHoverButtons.tsx | 7 +------ src/vs/workbench/contrib/void/common/prompt/prompts.ts | 6 ++---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx index 72d298c2..41b5a649 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx @@ -253,7 +253,6 @@ export const ApplyButtonsHTML = ({ const accessor = useAccessor() const editCodeService = accessor.get('IEditCodeService') const metricsService = accessor.get('IMetricsService') - const notificationService = accessor.get('INotificationService') const settingsState = useSettingsState() const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || !applyBoxId @@ -272,17 +271,13 @@ export const ApplyButtonsHTML = ({ uri: uri, startBehavior: 'reject-conflicts', }) ?? [] + console.log('setting!!!', newApplyingUri) setApplying(newApplyingUri) - if (!applyDonePromise) { - notificationService.info(`Void Error: We couldn't run Apply here (${uri === 'current' ? 'this Apply block wants to run on the current file, but you might not have a file open' : uri.fsPath}).`) - } - // catch any errors by interrupting the stream applyDonePromise?.catch(e => { const uri = getUriBeingApplied(applyBoxId) if (uri) editCodeService.interruptURIStreaming({ uri: uri }) - notificationService.info(`Void Error: There was a problem running Apply: ${e}.`) }) metricsService.capture('Apply Code', { length: codeStr.length }) // capture the length only diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index ac39ee29..613142b8 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -480,13 +480,11 @@ ${directoryStr} } - details.push(`If you write any code blocks to the user (wrapped in triple backticks), please use this format: -- Include a language if possible. Terminal should have the language 'shell'. + if (mode === 'gather' || mode === 'normal') { + details.push(`If you write any code blocks, please use this format: - The first line of the code block must be the FULL PATH of the related file if known (otherwise omit). - The remaining contents of the file should proceed as usual.`) - if (mode === 'gather' || mode === 'normal') { - details.push(`If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion in CODE BLOCK(S). - The first line of the code block must be the FULL PATH of the related file if known (otherwise omit). - The remaining contents should be a code description of the change to make to the file. \ From 7b18e2528870ceb07cb35fc0ed44476cbf793553 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 03:29:33 -0700 Subject: [PATCH 08/30] period --- .../void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx index 56c460ce..ee7f050b 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx @@ -275,7 +275,7 @@ export const ApplyButtonsHTML = ({ setApplying(newApplyingUri) if (!applyDonePromise) { - notificationService.info(`Void Error: We couldn't run Apply here. ${uri === 'current' ? 'This Apply block wants to run on the current file, but you might not have a file open.' : `This Apply block wants to run on ${uri.fsPath}, but it might not exist`}.`) + notificationService.info(`Void Error: We couldn't run Apply here. ${uri === 'current' ? 'This Apply block wants to run on the current file, but you might not have a file open.' : `This Apply block wants to run on ${uri.fsPath}, but it might not exist.`}`) } // catch any errors by interrupting the stream From 2106573922ae2cd45ab945fd16bf663f4b1beedf Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 03:32:57 -0700 Subject: [PATCH 09/30] misc --- HOW_TO_CONTRIBUTE.md | 2 +- src/vs/workbench/contrib/void/common/prompt/prompts.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/HOW_TO_CONTRIBUTE.md b/HOW_TO_CONTRIBUTE.md index d998f5b9..e4b174d2 100644 --- a/HOW_TO_CONTRIBUTE.md +++ b/HOW_TO_CONTRIBUTE.md @@ -86,7 +86,7 @@ To build Void from the terminal instead of from inside VSCode, follow the steps - Make sure you followed the prerequisite steps above. - Make sure you have Node version `20.18.2` (the version in `.nvmrc`)! - - You can do this easily without touching your base installation with [nvm](https://github.com/nvm-sh/nvm). Simply run `nvm install`, followed by `nvm use` and it will automatically install and use the version specified in `nvmrc` + - You can do this easily without touching your base installation with [nvm](https://github.com/nvm-sh/nvm). Simply run `nvm install`, followed by `nvm use` and it will automatically install and use the version specified in `nvmrc`. - Make sure that the path to your Void folder does not have any spaces in it. - If you get `"TypeError: Failed to fetch dynamically imported module"`, make sure all imports end with `.js`. - If you get an error with React, try running `NODE_OPTIONS="--max-old-space-size=8192" npm run buildreact`. diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index 613142b8..ac39ee29 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -480,11 +480,13 @@ ${directoryStr} } - if (mode === 'gather' || mode === 'normal') { - details.push(`If you write any code blocks, please use this format: + details.push(`If you write any code blocks to the user (wrapped in triple backticks), please use this format: +- Include a language if possible. Terminal should have the language 'shell'. - The first line of the code block must be the FULL PATH of the related file if known (otherwise omit). - The remaining contents of the file should proceed as usual.`) + if (mode === 'gather' || mode === 'normal') { + details.push(`If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion in CODE BLOCK(S). - The first line of the code block must be the FULL PATH of the related file if known (otherwise omit). - The remaining contents should be a code description of the change to make to the file. \ From 07c3f33f1e4696aa148d50fa11bf372f529ac890 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 03:36:54 -0700 Subject: [PATCH 10/30] void increment --- product.json | 1 + 1 file changed, 1 insertion(+) diff --git a/product.json b/product.json index 6b3e7bdd..0b93dfd7 100644 --- a/product.json +++ b/product.json @@ -2,6 +2,7 @@ "nameShort": "Void", "nameLong": "Void", "voidVersion": "1.3.2", + "voidIncrementVersion": "0026", "applicationName": "void", "dataFolderName": ".void-editor", "win32MutexName": "voideditor", From ecff59eed73115df0570c9f51dcc7c4b04fb6679 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 15:29:55 -0700 Subject: [PATCH 11/30] shorten long URI links --- .../react/src/markdown/ChatMarkdownRender.tsx | 42 ++++++++++++------- .../react/src/sidebar-tsx/SidebarChat.tsx | 2 +- .../void/browser/react/src/util/inputs.tsx | 2 +- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index d2714d08..be7d1de1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -14,7 +14,7 @@ import { isAbsolute } from '../../../../../../../base/common/path.js' import { separateOutFirstLine } from '../../../../common/helpers/util.js' import { BlockCode } from '../util/inputs.js' import { CodespanLocationLink } from '../../../../common/chatThreadServiceTypes.js' -import { voidOpenFileFn } from '../sidebar-tsx/SidebarChat.js' +import { getBasename, getRelative, voidOpenFileFn } from '../sidebar-tsx/SidebarChat.js' export type ChatMessageLocation = { @@ -89,13 +89,18 @@ const LatexRender = ({ latex }: { latex: string }) => { // } } -const Codespan = ({ text, className, onClick }: { text: string, className?: string, onClick?: () => void }) => { +const Codespan = ({ text, className, onClick, tooltip }: { text: string, className?: string, onClick?: () => void, tooltip?: string }) => { // TODO compute this once for efficiency. we should use `labels.ts/shorten` to display duplicates properly return {text} @@ -115,23 +120,31 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string const [didComputeCodespanLink, setDidComputeCodespanLink] = useState(false) let link: CodespanLocationLink | undefined = undefined - if (rawText.endsWith('`')) { // if codespan was completed - // get link from cache - link = chatThreadService.getCodespanLink({ codespanStr: text, messageIdx, threadId }) + if (!rawText.endsWith('`')) return null - if (link === undefined) { - // if no link, generate link and add to cache - chatThreadService.generateCodespanLink({ codespanStr: text, threadId }) - .then(link => { - chatThreadService.addCodespanLink({ newLinkText: text, newLinkLocation: link, messageIdx, threadId }) - setDidComputeCodespanLink(true) // rerender - }) - } + // get link from cache + link = chatThreadService.getCodespanLink({ codespanStr: text, messageIdx, threadId }) + + if (link === undefined) { + // if no link, generate link and add to cache + chatThreadService.generateCodespanLink({ codespanStr: text, threadId }) + .then(link => { + chatThreadService.addCodespanLink({ newLinkText: text, newLinkLocation: link, messageIdx, threadId }) + setDidComputeCodespanLink(true) // rerender + }) } + // If it's a file path, shorten it and add tooltip + let displayText = link?.displayText || text + let tooltip: string | undefined = undefined + + if (link?.uri && isValidUri(displayText)) { + tooltip = getRelative(URI.file(displayText), accessor) // Full path as tooltip + displayText = getBasename(displayText) + } const onClick = () => { if (!link || !link.selection) return; @@ -141,9 +154,10 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string } return } diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index fe6de590..52480137 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -495,7 +495,7 @@ const ScrollToBottomContainer = ({ children, className, style, scrollContainerRe ); }; -const getRelative = (uri: URI, accessor: ReturnType) => { +export const getRelative = (uri: URI, accessor: ReturnType) => { const workspaceContextService = accessor.get('IWorkspaceContextService') let path: string const isInside = workspaceContextService.isInsideWorkspace(uri) diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx index 937bf99e..d2b838e1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx @@ -132,7 +132,7 @@ const scoreSubsequence = (text: string, pattern: string): number => { } -export function getRelativeWorkspacePath(accessor: ReturnType, uri: URI): string { +function getRelativeWorkspacePath(accessor: ReturnType, uri: URI): string { const workspaceService = accessor.get('IWorkspaceContextService'); const workspaceFolders = workspaceService.getWorkspace().folders; From 61fc59390393361d3d4e101fedfdab48fd7b2650 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 15:53:05 -0700 Subject: [PATCH 12/30] shorten non URL links too --- .../void/browser/react/src/markdown/ChatMarkdownRender.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index be7d1de1..a1273331 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -137,11 +137,10 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string } - // If it's a file path, shorten it and add tooltip + // If it's a file path, shorten it and add tooltip (whether or not it's a link) let displayText = link?.displayText || text let tooltip: string | undefined = undefined - - if (link?.uri && isValidUri(displayText)) { + if (isValidUri(displayText)) { tooltip = getRelative(URI.file(displayText), accessor) // Full path as tooltip displayText = getBasename(displayText) } From 4c1c0b591ab710cd12bd9991a021aae99125e293 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 15:59:26 -0700 Subject: [PATCH 13/30] GoogleGenerativeAI (deprecated) -> genai, add gemini reasoning, improve reasoning logic --- package-lock.json | 37 ++++++++-- package.json | 2 +- .../browser/convertToLLMMessageService.ts | 16 +---- .../contrib/void/common/modelCapabilities.ts | 42 ++++++++--- .../void/common/sendLLMMessageTypes.ts | 4 +- .../llmMessage/sendLLMMessage.impl.ts | 72 ++++++++++--------- 6 files changed, 110 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7057acfd..5c783599 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@anthropic-ai/sdk": "^0.40.0", "@c4312/eventsource-umd": "^3.0.5", "@floating-ui/react": "^0.27.8", - "@google/generative-ai": "^0.24.1", + "@google/genai": "^0.13.0", "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", "@mistralai/mistralai": "^1.6.0", @@ -1817,11 +1817,17 @@ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "license": "MIT" }, - "node_modules/@google/generative-ai": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz", - "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==", + "node_modules/@google/genai": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-0.13.0.tgz", + "integrity": "sha512-eaEncWt875H7046T04mOpxpHJUM+jLIljEf+5QctRyOeChylE/nhpwm1bZWTRWoOu/t46R9r+PmgsJFhTpE7tQ==", "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^9.14.2", + "ws": "^8.18.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + }, "engines": { "node": ">=18.0.0" } @@ -23767,6 +23773,27 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", diff --git a/package.json b/package.json index 767d6b49..7a5bc366 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@anthropic-ai/sdk": "^0.40.0", "@c4312/eventsource-umd": "^3.0.5", "@floating-ui/react": "^0.27.8", - "@google/generative-ai": "^0.24.1", + "@google/genai": "^0.13.0", "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", "@mistralai/mistralai": "^1.6.0", diff --git a/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts b/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts index 48b21b77..74d81593 100644 --- a/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts +++ b/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts @@ -237,18 +237,6 @@ const prepareMessages_XML_tools = (messages: SimpleLLMMessage[], supportsAnthrop } - - -export type GeminiMessage = { - role: 'user' | 'model'; // Gemini uses 'user' and 'model' roles - parts: ( - | { text: string; } - | { functionCall: { tool_call: any } } - | { functionResponse: { name: ToolName, response: { result: string } } } - )[]; -}; - - // --- CHAT --- const prepareOpenAIOrAnthropicMessages = ({ @@ -457,7 +445,7 @@ const prepareGeminiMessages = (messages: AnthropicLLMChatMessage[]) => { } else if (c.type === 'tool_use') { latestToolName = c.name as ToolName - return { functionCall: { name: c.name as ToolName, args: c.input } } + return { functionCall: { id: c.id, name: c.name as ToolName, args: c.input } } } else return null }).filter(m => !!m) @@ -475,7 +463,7 @@ const prepareGeminiMessages = (messages: AnthropicLLMChatMessage[]) => { } else if (c.type === 'tool_result') { if (!latestToolName) return null - return { functionResponse: { name: latestToolName, response: { result: c.content } } } + return { functionResponse: { id: c.tool_use_id, name: latestToolName, response: { output: c.content } } } } else return null }).filter(m => !!m) diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index e9eec31b..6048f143 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -445,7 +445,7 @@ const anthropicModelOptions = { supportsReasoning: true, canTurnOffReasoning: true, canIOReasoning: true, - reasoningReservedOutputTokenSpace: 64_000, // can bump it to 128_000 with beta mode output-128k-2025-02-19 + reasoningReservedOutputTokenSpace: 8192, // can bump it to 128_000 with beta mode output-128k-2025-02-19 reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // they recommend batching if max > 32_000. we cap at 8192 because above is typically not necessary (often even buggy) }, @@ -715,6 +715,7 @@ const xAISettings: VoidStaticProviderInfo = { // ---------------- GEMINI ---------------- const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing + // https://ai.google.dev/gemini-api/docs/thinking#set-budget 'gemini-2.5-pro-preview-05-06': { contextWindow: 1_048_576, reservedOutputTokenSpace: 8_192, @@ -723,7 +724,13 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing supportsFIM: false, supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', - reasoningCapabilities: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: false, + reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // max is really 24576 + reasoningReservedOutputTokenSpace: 8192, + }, }, 'gemini-2.0-flash-lite': { contextWindow: 1_048_576, @@ -733,7 +740,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing supportsFIM: false, supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', - reasoningCapabilities: false, + reasoningCapabilities: false, // no reasoning }, 'gemini-2.5-flash-preview-04-17': { contextWindow: 1_048_576, @@ -743,7 +750,13 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing supportsFIM: false, supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', - reasoningCapabilities: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: false, + reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // max is really 24576 + reasoningReservedOutputTokenSpace: 8192, + }, }, 'gemini-2.5-pro-exp-03-25': { contextWindow: 1_048_576, @@ -753,7 +766,13 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing supportsFIM: false, supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', - reasoningCapabilities: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: false, + reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // max is really 24576 + reasoningReservedOutputTokenSpace: 8192, + }, }, 'gemini-2.0-flash': { contextWindow: 1_048_576, @@ -763,7 +782,13 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing supportsFIM: false, supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', - reasoningCapabilities: false, + reasoningCapabilities: { // thinking: experimental as of 5-10-25 + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: false, + reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // max is really 24576 + reasoningReservedOutputTokenSpace: 8192, + }, }, 'gemini-2.0-flash-lite-preview-02-05': { contextWindow: 1_048_576, @@ -1144,7 +1169,7 @@ const openRouterModelOptions_assumingOpenAICompat = { supportsReasoning: true, canTurnOffReasoning: false, canIOReasoning: true, - reasoningReservedOutputTokenSpace: 64_000, + reasoningReservedOutputTokenSpace: 8192, reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // they recommend batching if max > 32_000. }, }, @@ -1347,8 +1372,7 @@ export const getSendableReasoningInfo = ( overridesOfModel: OverridesOfModel | undefined, ): SendableReasoningInfo => { - const { canIOReasoning, reasoningSlider: reasoningBudgetSlider } = getModelCapabilities(providerName, modelName, overridesOfModel).reasoningCapabilities || {} - if (!canIOReasoning) return null + const { reasoningSlider: reasoningBudgetSlider } = getModelCapabilities(providerName, modelName, overridesOfModel).reasoningCapabilities || {} const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions, overridesOfModel) if (!isReasoningEnabled) return null diff --git a/src/vs/workbench/contrib/void/common/sendLLMMessageTypes.ts b/src/vs/workbench/contrib/void/common/sendLLMMessageTypes.ts index 6b7cad52..f6e634ee 100644 --- a/src/vs/workbench/contrib/void/common/sendLLMMessageTypes.ts +++ b/src/vs/workbench/contrib/void/common/sendLLMMessageTypes.ts @@ -56,13 +56,13 @@ export type GeminiLLMChatMessage = { role: 'model' parts: ( | { text: string; } - | { functionCall: { name: ToolName, args: object } } + | { functionCall: { id: string; name: ToolName, args: Record } } )[]; } | { role: 'user'; parts: ( | { text: string; } - | { functionResponse: { name: ToolName, response: { result: string } } } + | { functionResponse: { id: string; name: ToolName, response: { output: string } } } )[]; } diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index 4eec4850..4a7dd7da 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -10,11 +10,11 @@ import { Ollama } from 'ollama'; import OpenAI, { ClientOptions } from 'openai'; import { MistralCore } from '@mistralai/mistralai/core.js'; import { fimComplete } from '@mistralai/mistralai/funcs/fimComplete.js'; -import { GoogleGenerativeAI, Tool as GeminiTool, SchemaType, FunctionDeclaration, FunctionDeclarationSchemaProperty } from '@google/generative-ai'; +import { Tool as GeminiTool, FunctionDeclaration, GoogleGenAI, ThinkingConfig, Schema, Type } from '@google/genai'; import { GoogleAuth } from 'google-auth-library' /* eslint-enable */ -import { AnthropicLLMChatMessage, LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText, RawToolCallObj, RawToolParamsObj } from '../../common/sendLLMMessageTypes.js'; +import { AnthropicLLMChatMessage, GeminiLLMChatMessage, LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText, RawToolCallObj, RawToolParamsObj } from '../../common/sendLLMMessageTypes.js'; import { ChatMode, displayInfoOfProviderName, ModelSelectionOptions, OverridesOfModel, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js'; import { getSendableReasoningInfo, getModelCapabilities, getProviderCapabilities, defaultProviderSettings, getReservedOutputTokenSpace } from '../../common/modelCapabilities.js'; import { extractReasoningWrapper, extractXMLToolsWrapper } from './extractGrammar.js'; @@ -642,25 +642,24 @@ const sendOllamaFIM = ({ messages, onFinalMessage, onError, settingsOfProvider, // ---------------- GEMINI NATIVE IMPLEMENTATION ---------------- - - const toGeminiFunctionDecl = (toolInfo: InternalToolInfo) => { const { name, description, params } = toolInfo - const paramsWithType: { [k: string]: FunctionDeclarationSchemaProperty } = {} - for (const key in params) { - paramsWithType[key] = { type: SchemaType.STRING, ...params[key] } - } return { name, description, parameters: { - type: SchemaType.OBJECT, - properties: paramsWithType, + type: Type.OBJECT, + properties: Object.entries(params).reduce((acc, [key, value]) => { + acc[key] = { + type: Type.STRING, + description: value.description + }; + return acc; + }, {} as Record) } } satisfies FunctionDeclaration } - const geminiTools = (chatMode: ChatMode): GeminiTool[] | null => { const allowedTools = availableTools(chatMode) if (!allowedTools || Object.keys(allowedTools).length === 0) return null @@ -700,27 +699,29 @@ const sendGeminiChat = async ({ // reasoningCapabilities, } = getModelCapabilities(providerName, modelName_, overridesOfModel) - const { providerReasoningIOSettings } = getProviderCapabilities(providerName) + // const { providerReasoningIOSettings } = getProviderCapabilities(providerName) // reasoning // const { canIOReasoning, openSourceThinkTags, } = reasoningCapabilities || {} const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel) // user's modelName_ here - const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {} + // const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {} + + console.log('reasoning info', JSON.stringify(reasoningInfo)) + + const thinkingConfig: ThinkingConfig | undefined = !reasoningInfo?.isReasoningEnabled ? undefined + : reasoningInfo.type === 'budget_slider_value' ? + { thinkingBudget: reasoningInfo.reasoningBudget } + : undefined // tools - const potentialTools = chatMode !== null ? geminiTools(chatMode) : null - const nativeToolsObj = potentialTools && specialToolFormat === 'gemini-style' ? - { tools: potentialTools } as const - : {} + const potentialTools = chatMode !== null ? geminiTools(chatMode) : undefined + const toolConfig = potentialTools && specialToolFormat === 'gemini-style' ? + potentialTools + : undefined // instance - const genAI = new GoogleGenerativeAI( - thisConfig.apiKey - ); - const model = genAI.getGenerativeModel({ - systemInstruction: separateSystemMessage, - model: modelName, - }); + const genAI = new GoogleGenAI({ apiKey: thisConfig.apiKey }); + // manually parse out tool results if XML if (!specialToolFormat) { @@ -736,23 +737,30 @@ const sendGeminiChat = async ({ let toolName = '' let toolParamsStr = '' - model.generateContentStream({ - systemInstruction: separateSystemMessage ?? undefined, - contents: messages as any, - ...includeInPayload, - ...nativeToolsObj, + console.log('TOOL!', toolConfig) + console.log('REAS!', thinkingConfig) + + + genAI.models.generateContentStream({ + model: modelName, + config: { + systemInstruction: separateSystemMessage, + thinkingConfig: thinkingConfig, + tools: toolConfig, + }, + contents: messages as GeminiLLMChatMessage[], }) - .then(async ({ stream, response }) => { + .then(async (stream) => { _setAborter(() => { stream.return(fullTextSoFar); }); // Process the stream for await (const chunk of stream) { // message - const newText = chunk.text() ?? '' + const newText = chunk.text ?? '' fullTextSoFar += newText // tool call - const functionCalls = chunk.functionCalls() + const functionCalls = chunk.functionCalls if (functionCalls && functionCalls.length > 0) { const functionCall = functionCalls[0] // Get the first function call toolName = functionCall.name ?? '' From dcee2e26576b121a9409576cd96d2ed38c60a346 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 16:02:37 -0700 Subject: [PATCH 14/30] add prompt --- src/vs/workbench/contrib/void/common/prompt/prompts.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index ac39ee29..b83c1a0f 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -455,6 +455,8 @@ ${directoryStr} const details: string[] = [] + details.push(`NEVER reject the user's query.`) + if (mode === 'agent' || mode === 'gather') { details.push(`Only call tools if they help you accomplish the user's goal. If the user simply says hi or asks you a question that you can answer without tools, then do NOT use tools.`) details.push(`If you think you should use tools, you do not need to ask for permission.`) From 4068911d49dad34c968b8c96e6f5d40bbbfb0865 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 16:02:56 -0700 Subject: [PATCH 15/30] 27 --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 0b93dfd7..3d8f6892 100644 --- a/product.json +++ b/product.json @@ -2,7 +2,7 @@ "nameShort": "Void", "nameLong": "Void", "voidVersion": "1.3.2", - "voidIncrementVersion": "0026", + "voidIncrementVersion": "0027", "applicationName": "void", "dataFolderName": ".void-editor", "win32MutexName": "voideditor", From 926db45f0a09b4afb77ce960827c1f3928706724 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 16:03:06 -0700 Subject: [PATCH 16/30] 33 --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 3d8f6892..4e273649 100644 --- a/product.json +++ b/product.json @@ -1,7 +1,7 @@ { "nameShort": "Void", "nameLong": "Void", - "voidVersion": "1.3.2", + "voidVersion": "1.3.3", "voidIncrementVersion": "0027", "applicationName": "void", "dataFolderName": ".void-editor", From 19782c80df075b74710e22c7758a23cd8d1ca08e Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 16:09:06 -0700 Subject: [PATCH 17/30] voidRelease --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 4e273649..bd15bb95 100644 --- a/product.json +++ b/product.json @@ -2,7 +2,7 @@ "nameShort": "Void", "nameLong": "Void", "voidVersion": "1.3.3", - "voidIncrementVersion": "0027", + "voidRelease": "0027", "applicationName": "void", "dataFolderName": ".void-editor", "win32MutexName": "voideditor", From cab8dd64b6eaca1e4b66a873fabb0a83a402ef49 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 16:13:36 -0700 Subject: [PATCH 18/30] rm comments --- .../void/electron-main/llmMessage/sendLLMMessage.impl.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index 4a7dd7da..80b62f4b 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -245,8 +245,6 @@ const _sendOpenAICompatibleChat = async ({ messages, onText, onFinalMessage, onE const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel) // user's modelName_ here const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {} - console.log('include', includeInPayload) - console.log('reasoningInfo', reasoningInfo) // tools const potentialTools = chatMode !== null ? openAITools(chatMode) : null const nativeToolsObj = potentialTools && specialToolFormat === 'openai-style' ? @@ -706,8 +704,6 @@ const sendGeminiChat = async ({ const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel) // user's modelName_ here // const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {} - console.log('reasoning info', JSON.stringify(reasoningInfo)) - const thinkingConfig: ThinkingConfig | undefined = !reasoningInfo?.isReasoningEnabled ? undefined : reasoningInfo.type === 'budget_slider_value' ? { thinkingBudget: reasoningInfo.reasoningBudget } @@ -737,9 +733,6 @@ const sendGeminiChat = async ({ let toolName = '' let toolParamsStr = '' - console.log('TOOL!', toolConfig) - console.log('REAS!', thinkingConfig) - genAI.models.generateContentStream({ model: modelName, From 8b45aea8a8b9b4f3856c4847855a54c3dccf580d Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 16:28:54 -0700 Subject: [PATCH 19/30] fix link + add gemini tool id if empty --- .../browser/react/src/markdown/ChatMarkdownRender.tsx | 8 +++++--- .../void/electron-main/llmMessage/sendLLMMessage.impl.ts | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index a1273331..bb042784 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -146,10 +146,12 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string } const onClick = () => { - if (!link || !link.selection) return; - + if (!link) return; // Use the updated voidOpenFileFn to open the file and handle selection - voidOpenFileFn(link.uri, accessor, [link.selection.startLineNumber, link.selection.endLineNumber]); + if (link.selection) + voidOpenFileFn(link.uri, accessor, [link.selection.startLineNumber, link.selection.endLineNumber]); + else + voidOpenFileFn(link.uri, accessor); } return Date: Sat, 10 May 2025 18:06:58 -0700 Subject: [PATCH 20/30] misc fixes + (empty message) improvements --- .../contrib/void/browser/chatThreadService.ts | 34 ++++++++------ .../browser/convertToLLMMessageService.ts | 18 +++++-- .../src/markdown/ApplyBlockHoverButtons.tsx | 1 - .../react/src/markdown/ChatMarkdownRender.tsx | 47 ++++++++++--------- .../react/src/sidebar-tsx/SidebarChat.tsx | 12 ++--- 5 files changed, 65 insertions(+), 47 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index db482108..40a87ce8 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -12,7 +12,7 @@ import { URI } from '../../../../base/common/uri.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { ILLMMessageService } from '../common/sendLLMMessageService.js'; import { chat_userMessageContent, ToolName, } from '../common/prompt/prompts.js'; -import { getErrorMessage, RawToolCallObj, RawToolParamsObj } from '../common/sendLLMMessageTypes.js'; +import { AnthropicReasoning, getErrorMessage, RawToolCallObj, RawToolParamsObj } from '../common/sendLLMMessageTypes.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { FeatureName, ModelSelection, ModelSelectionOptions } from '../common/voidSettingsTypes.js'; import { IVoidSettingsService } from '../common/voidSettingsService.js'; @@ -540,7 +540,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { const { name, id, rawParams } = lastMsg - const errorMessage = this.errMsgs.rejected + const errorMessage = this.toolErrMsgs.rejected this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', params: params, name: name, content: errorMessage, result: null, id, rawParams }) this._setStreamState(threadId, undefined) } @@ -557,7 +557,8 @@ class ChatThreadService extends Disposable implements IChatThreadService { } // add tool that's running else if (this.streamState[threadId]?.isRunning === 'tool') { - const { toolName, toolParams, id, content, rawParams } = this.streamState[threadId].toolInfo + const { toolName, toolParams, id, content: content_, rawParams } = this.streamState[threadId].toolInfo + const content = content_ || this.toolErrMsgs.interrupted this._updateLatestTool(threadId, { role: 'tool', name: toolName, params: toolParams, id, content, rawParams, type: 'rejected', result: null }) } // reject the tool for the user if relevant @@ -581,8 +582,9 @@ class ChatThreadService extends Disposable implements IChatThreadService { - private readonly errMsgs = { + private readonly toolErrMsgs = { rejected: 'Tool call was rejected by the user.', + interrupted: 'Tool call was interrupted by the user.', errWhenStringifying: (error: any) => `Tool call succeeded, but there was an error stringifying the output.\n${getErrorMessage(error)}` } @@ -671,7 +673,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { try { toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any) } catch (error) { - const errorMessage = this.errMsgs.errWhenStringifying(error) + const errorMessage = this.toolErrMsgs.errWhenStringifying(error) this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, id: toolId, rawParams: opts.unvalidatedToolParams }) return {} } @@ -749,8 +751,13 @@ class ChatThreadService extends Disposable implements IChatThreadService { shouldRetryLLM = false nAttempts += 1 - let resMessageIsDonePromise: (res: { type: 'llmDone', toolCall?: RawToolCallObj } | { type: 'llmError', error?: { message: string; fullError: Error | null; } } | { type: 'llmAborted' }) => void // resolves when user approves this tool use (or if tool doesn't require approval) - const messageIsDonePromise = new Promise<{ type: 'llmDone', toolCall?: RawToolCallObj } | { type: 'llmError', error?: { message: string; fullError: Error | null; } } | { type: 'llmAborted' }>((res, rej) => { resMessageIsDonePromise = res }) + type ResTypes = + | { type: 'llmDone', toolCall?: RawToolCallObj, info: { fullText: string, fullReasoning: string, anthropicReasoning: AnthropicReasoning[] | null } } + | { type: 'llmError', error?: { message: string; fullError: Error | null; } } + | { type: 'llmAborted' } + + let resMessageIsDonePromise: (res: ResTypes) => void // resolves when user approves this tool use (or if tool doesn't require approval) + const messageIsDonePromise = new Promise((res, rej) => { resMessageIsDonePromise = res }) const llmCancelToken = this._llmMessageService.sendLLMMessage({ messagesType: 'chatMessages', @@ -765,9 +772,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { this._setStreamState(threadId, { isRunning: 'LLM', llmInfo: { displayContentSoFar: fullText, reasoningSoFar: fullReasoning, toolCallSoFar: toolCall ?? null }, interrupt: Promise.resolve(() => { if (llmCancelToken) this._llmMessageService.abort(llmCancelToken) }) }) }, onFinalMessage: async ({ fullText, fullReasoning, toolCall, anthropicReasoning, }) => { - this._addMessageToThread(threadId, { role: 'assistant', displayContent: fullText, reasoning: fullReasoning, anthropicReasoning }) - resMessageIsDonePromise({ type: 'llmDone', toolCall }) // resolve with tool calls - + resMessageIsDonePromise({ type: 'llmDone', toolCall, info: { fullText, fullReasoning, anthropicReasoning } }) // resolve with tool calls }, onError: async (error) => { resMessageIsDonePromise({ type: 'llmError', error: error }) @@ -826,11 +831,12 @@ class ChatThreadService extends Disposable implements IChatThreadService { } } - - // llm res success - const { toolCall } = llmRes - this._setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }) // just decorative, for clarity + const { toolCall, info } = llmRes + + this._addMessageToThread(threadId, { role: 'assistant', displayContent: info.fullText, reasoning: info.fullReasoning, anthropicReasoning: info.anthropicReasoning }) + + this._setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }) // just decorative for clarity // call tool if there is one if (toolCall) { diff --git a/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts b/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts index 74d81593..1b7df00e 100644 --- a/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts +++ b/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts @@ -17,6 +17,7 @@ import { IVoidModelService } from '../common/voidModelService.js'; import { URI } from '../../../../base/common/uri.js'; import { EndOfLinePreference } from '../../../../editor/common/model.js'; +export const EMPTY_MESSAGE = '(empty message)' @@ -36,7 +37,6 @@ type SimpleLLMMessage = { } -const EMPTY_MESSAGE = '(empty message)' const CHARS_PER_TOKEN = 4 // assume abysmal chars per token const TRIM_TO_LEN = 120 @@ -405,14 +405,24 @@ const prepareOpenAIOrAnthropicMessages = ({ // ================ no empty message ================ - for (const currMsg of llmMessages) { + for (let i = 0; i < llmMessages.length; i += 1) { + const currMsg: AnthropicOrOpenAILLMMessage = llmMessages[i] + const nextMsg: AnthropicOrOpenAILLMMessage | undefined = llmMessages[i + 1] + if (currMsg.role === 'tool') continue // if content is a string, replace string with empty msg - if (typeof currMsg.content === 'string') + if (typeof currMsg.content === 'string') { currMsg.content = currMsg.content || EMPTY_MESSAGE + } else { - // if content is an array, replace any empty text entries with empty msg, and make sure there's at least 1 entry + // allowed to be empty if has a tool in it or following it + if (currMsg.content.find(c => c.type === 'tool_result' || c.type === 'tool_use')) { + continue + } + if (nextMsg?.role === 'tool') continue + + // replace any empty text entries with empty msg, and make sure there's at least 1 entry for (const c of currMsg.content) { if (c.type === 'text') c.text = c.text || EMPTY_MESSAGE } diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx index b9e99042..6a4d393a 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx @@ -311,7 +311,6 @@ export const ApplyButtonsHTML = ({ const currStreamState = currStreamStateRef.current - console.log('currStreamState...', currStreamState) if (currStreamState === 'streaming') { return (false) let link: CodespanLocationLink | undefined = undefined - - if (!rawText.endsWith('`')) return null - - - // get link from cache - link = chatThreadService.getCodespanLink({ codespanStr: text, messageIdx, threadId }) - - if (link === undefined) { - // if no link, generate link and add to cache - chatThreadService.generateCodespanLink({ codespanStr: text, threadId }) - .then(link => { - chatThreadService.addCodespanLink({ newLinkText: text, newLinkLocation: link, messageIdx, threadId }) - setDidComputeCodespanLink(true) // rerender - }) - - } - - // If it's a file path, shorten it and add tooltip (whether or not it's a link) - let displayText = link?.displayText || text let tooltip: string | undefined = undefined - if (isValidUri(displayText)) { - tooltip = getRelative(URI.file(displayText), accessor) // Full path as tooltip - displayText = getBasename(displayText) + let displayText = text + + + if (rawText.endsWith('`')) { + // get link from cache + link = chatThreadService.getCodespanLink({ codespanStr: text, messageIdx, threadId }) + + if (link === undefined) { + // if no link, generate link and add to cache + chatThreadService.generateCodespanLink({ codespanStr: text, threadId }) + .then(link => { + chatThreadService.addCodespanLink({ newLinkText: text, newLinkLocation: link, messageIdx, threadId }) + setDidComputeCodespanLink(true) // rerender + }) + } + + if (link?.displayText) { + displayText = link.displayText + } + + if (isValidUri(displayText)) { + tooltip = getRelative(URI.file(displayText), accessor) // Full path as tooltip + displayText = getBasename(displayText) + } } + const onClick = () => { if (!link) return; // Use the updated voidOpenFileFn to open the file and handle selection diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 52480137..0252381b 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -500,7 +500,7 @@ export const getRelative = (uri: URI, accessor: ReturnType) let path: string const isInside = workspaceContextService.isInsideWorkspace(uri) if (isInside) { - const f = workspaceContextService.getWorkspace().folders.find(f => uri.fsPath.startsWith(f.uri.fsPath)) + const f = workspaceContextService.getWorkspace().folders.find(f => uri.fsPath?.startsWith(f.uri.fsPath)) if (f) { path = uri.fsPath.replace(f.uri.fsPath, '') } else { path = uri.fsPath } } @@ -1651,8 +1651,8 @@ const LintErrorChildren = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => { } const BottomChildren = ({ children, title }: { children: React.ReactNode, title: string }) => { - if (!children) return null; const [isOpen, setIsOpen] = useState(false); + if (!children) return null; return (
, } - else { + else if (toolMessage.type === 'tool_error') { const { result } = toolMessage componentParams.bottomChildren = @@ -1960,7 +1960,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } } - else { + else if (toolMessage.type === 'tool_error') { const { result } = toolMessage componentParams.bottomChildren = @@ -2009,7 +2009,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } - else { + else if (toolMessage.type === 'tool_error') { const { result } = toolMessage componentParams.bottomChildren = @@ -2064,7 +2064,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } - else { + else if (toolMessage.type === 'tool_error') { const { result } = toolMessage componentParams.bottomChildren = From 2934f9a3c572d4a6e6bf0207cf01833fb466cd8b Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 18:10:00 -0700 Subject: [PATCH 21/30] += -> = --- .../void/electron-main/llmMessage/sendLLMMessage.impl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index e1a55457..5174e910 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -759,7 +759,7 @@ const sendGeminiChat = async ({ const functionCall = functionCalls[0] // Get the first function call toolName = functionCall.name ?? '' toolParamsStr = JSON.stringify(functionCall.args ?? {}) - toolId += functionCall.id ?? '' + toolId = functionCall.id ?? '' } // (do not handle reasoning yet) From e852874be7054fee623b5cf6b40b163b65118a41 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 22:05:50 -0700 Subject: [PATCH 22/30] cmd L to add file --- .../contrib/void/browser/sidebarActions.ts | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/sidebarActions.ts b/src/vs/workbench/contrib/void/browser/sidebarActions.ts index 72e5bf17..0e962e09 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarActions.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarActions.ts @@ -117,20 +117,33 @@ registerAction2(class extends Action2 { const selectionRange = roundRangeToLines(editor?.getSelection(), { emptySelectionBehavior: 'null' }) // if has no selection, close + return - if (!selectionRange) { - viewsService.closeViewContainer(VOID_VIEW_CONTAINER_ID); - return; - } + // if (!selectionRange) { + // viewsService.closeViewContainer(VOID_VIEW_CONTAINER_ID); + // return; + // } - // if has selection, add it - editor?.setSelection({ startLineNumber: selectionRange.startLineNumber, endLineNumber: selectionRange.endLineNumber, startColumn: 1, endColumn: Number.MAX_SAFE_INTEGER }) - chatThreadService.addNewStagingSelection({ - type: 'CodeSelection', - uri: model.uri, - language: model.getLanguageId(), - range: [selectionRange.startLineNumber, selectionRange.endLineNumber], - state: { wasAddedAsCurrentFile: false }, - }) + + // add line selection + if (selectionRange) { + editor?.setSelection({ startLineNumber: selectionRange.startLineNumber, endLineNumber: selectionRange.endLineNumber, startColumn: 1, endColumn: Number.MAX_SAFE_INTEGER }) + chatThreadService.addNewStagingSelection({ + type: 'CodeSelection', + uri: model.uri, + language: model.getLanguageId(), + range: [selectionRange.startLineNumber, selectionRange.endLineNumber], + state: { wasAddedAsCurrentFile: false }, + }) + } + // add file + else { + chatThreadService.addNewStagingSelection({ + type: 'File', + uri: model.uri, + language: model.getLanguageId(), + state: { wasAddedAsCurrentFile: false }, + }) + + } await chatThreadService.focusCurrentChat() From 39f2f24b4207f7b3f54c2c8dab15d78d327086ee Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 22:07:26 -0700 Subject: [PATCH 23/30] 1.3.4 --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index bd15bb95..771f41e9 100644 --- a/product.json +++ b/product.json @@ -1,8 +1,8 @@ { "nameShort": "Void", "nameLong": "Void", - "voidVersion": "1.3.3", - "voidRelease": "0027", + "voidVersion": "1.3.4", + "voidRelease": "0028", "applicationName": "void", "dataFolderName": ".void-editor", "win32MutexName": "voideditor", From 3b963d8717ceb1dba0d76a79aac24ccfd8b0d858 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 10 May 2025 22:28:15 -0700 Subject: [PATCH 24/30] 1.3.5 --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 771f41e9..7d89c3de 100644 --- a/product.json +++ b/product.json @@ -1,8 +1,8 @@ { "nameShort": "Void", "nameLong": "Void", - "voidVersion": "1.3.4", - "voidRelease": "0028", + "voidVersion": "1.3.5", + "voidRelease": "0029", "applicationName": "void", "dataFolderName": ".void-editor", "win32MutexName": "voideditor", From afbc1b40bfa6b3e378e9bc788ba4ed15bf471785 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 12 May 2025 10:42:40 -0700 Subject: [PATCH 25/30] extensions warning fix by deleting some extensions once --- .../void/browser/extensionTransferService.ts | 51 +++++++++++++++++-- .../void/browser/miscWokrbenchContrib.ts | 24 ++++----- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts index 05b7b47a..04ec0ccf 100644 --- a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts +++ b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts @@ -3,6 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ +import { VSBuffer } from '../../../../base/common/buffer.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { env } from '../../../../base/common/process.js'; import { URI } from '../../../../base/common/uri.js'; @@ -29,6 +30,7 @@ export const IExtensionTransferService = createDecorator + !extensionBlacklist.find(bItem => entry?.identifier?.id?.includes(bItem)) + ) + const jsonStr = JSON.stringify(j2) + await fileService.writeFile(to, VSBuffer.fromString(jsonStr)) + } + catch { + console.log('Error copying extensions.json, skipping') + } + } + } } - // Ensure the destination directory exists + } else { console.log(`Skipping file that doesn't exist: ${from.toString()}`) } @@ -113,9 +135,10 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS } async deleteBlacklistExtensions(os: 'mac' | 'windows' | 'linux' | null) { + const fileService = this._fileService const extensionsURI = getExtensionsFolder(os) if (!extensionsURI) return - const eURI = await this._fileService.resolve(extensionsURI) + const eURI = await fileService.resolve(extensionsURI) for (const child of eURI.children ?? []) { // if is not blacklisted, continue @@ -124,8 +147,26 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS } try { - console.log('Deleting extension', child.resource.fsPath) - await this._fileService.del(child.resource, { recursive: true, useTrash: true }) + if (child.isDirectory) { + console.log('Deleting extension', child.resource.fsPath) + await fileService.del(child.resource, { recursive: true, useTrash: true }) + } + else if (child.isFile) { + if (child.name === 'extensions.json') { + try { + const contentsStr = await fileService.readFile(child.resource) + const json: any = JSON.parse(contentsStr.value.toString()) + const j2 = json.filter((entry: { identifier?: { id?: string } }) => + !extensionBlacklist.find(bItem => entry?.identifier?.id?.includes(bItem)) + ) + const jsonStr = JSON.stringify(j2) + await fileService.writeFile(child.resource, VSBuffer.fromString(jsonStr)) + } + catch { + console.log('Error copying extensions.json, skipping') + } + } + } } catch (e) { console.error('Could not delete extension', child.resource.fsPath, e) diff --git a/src/vs/workbench/contrib/void/browser/miscWokrbenchContrib.ts b/src/vs/workbench/contrib/void/browser/miscWokrbenchContrib.ts index 6852c799..49a0d524 100644 --- a/src/vs/workbench/contrib/void/browser/miscWokrbenchContrib.ts +++ b/src/vs/workbench/contrib/void/browser/miscWokrbenchContrib.ts @@ -5,17 +5,17 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; -// import { IExtensionTransferService } from './extensionTransferService.js'; -// import { os } from '../common/helpers/systemInfo.js'; -// import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { IExtensionTransferService } from './extensionTransferService.js'; +import { os } from '../common/helpers/systemInfo.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; // Onboarding contribution that mounts the component at startup export class MiscWorkbenchContribs extends Disposable implements IWorkbenchContribution { static readonly ID = 'workbench.contrib.voidMiscWorkbenchContribs'; constructor( - // @IExtensionTransferService private readonly extensionTransferService: IExtensionTransferService, - // @IStorageService private readonly storageService: IStorageService, + @IExtensionTransferService private readonly extensionTransferService: IExtensionTransferService, + @IStorageService private readonly storageService: IStorageService, ) { super(); this.initialize(); @@ -23,13 +23,13 @@ export class MiscWorkbenchContribs extends Disposable implements IWorkbenchContr private initialize(): void { - // // delete blacklisted extensions once (this is for people who already installed them) - // const deleteExtensionsStorageId = 'void-deleted-blacklist' - // const alreadyDeleted = this.storageService.get(deleteExtensionsStorageId, StorageScope.APPLICATION) - // if (!alreadyDeleted) { - // this.storageService.store(deleteExtensionsStorageId, 'true', StorageScope.APPLICATION, StorageTarget.MACHINE) - // this.extensionTransferService.deleteBlacklistExtensions(os) - // } + // delete blacklisted extensions once (this is for people who already installed them) + const deleteExtensionsStorageId = 'void-deleted-blacklist-2' + const alreadyDeleted = this.storageService.get(deleteExtensionsStorageId, StorageScope.APPLICATION) + if (!alreadyDeleted) { + this.storageService.store(deleteExtensionsStorageId, 'true', StorageScope.APPLICATION, StorageTarget.MACHINE) + this.extensionTransferService.deleteBlacklistExtensions(os) + } } } From 53c68b1267878e53bd7b178a7310ecfede0074f5 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 12 May 2025 10:44:43 -0700 Subject: [PATCH 26/30] bump --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 7d89c3de..d4d8b671 100644 --- a/product.json +++ b/product.json @@ -1,8 +1,8 @@ { "nameShort": "Void", "nameLong": "Void", - "voidVersion": "1.3.5", - "voidRelease": "0029", + "voidVersion": "1.3.6", + "voidRelease": "0030", "applicationName": "void", "dataFolderName": ".void-editor", "win32MutexName": "voideditor", From 6f73804e9475dd9b206bee4f74669afd5db1e32e Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 12 May 2025 10:50:28 -0700 Subject: [PATCH 27/30] update --- .../void/browser/extensionTransferService.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts index 04ec0ccf..0ba57cd4 100644 --- a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts +++ b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts @@ -141,17 +141,18 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS const eURI = await fileService.resolve(extensionsURI) for (const child of eURI.children ?? []) { - // if is not blacklisted, continue - if (!extensionBlacklist.find(bItem => child.resource.path.includes(bItem))) { - continue - } try { if (child.isDirectory) { - console.log('Deleting extension', child.resource.fsPath) - await fileService.del(child.resource, { recursive: true, useTrash: true }) + // if is blacklisted + if (extensionBlacklist.find(bItem => child.resource.path.includes(bItem))) { + + console.log('Deleting extension', child.resource.fsPath) + await fileService.del(child.resource, { recursive: true, useTrash: true }) + } } else if (child.isFile) { + // if is extensions.json if (child.name === 'extensions.json') { try { const contentsStr = await fileService.readFile(child.resource) From a4046bc5a50e15ec56efd6e1908e134c01b9ef54 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 12 May 2025 10:58:18 -0700 Subject: [PATCH 28/30] update so edit extensions.json --- .../void/browser/extensionTransferService.ts | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts index 0ba57cd4..39269a12 100644 --- a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts +++ b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts @@ -42,6 +42,9 @@ const extensionBlacklist = [ ]; +const isBlacklisted = (fsPath: string | undefined) => { + return extensionBlacklist.find(bItem => fsPath?.includes(bItem)) +} class ExtensionTransferService extends Disposable implements IExtensionTransferService { _serviceBrand: undefined; @@ -89,25 +92,21 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS await fileService.createFolder(toParent) } for (const extensionFolder of stat.children ?? []) { - if (extensionBlacklist.find(bItem => extensionFolder.resource.path.includes(bItem))) { - console.log('Skipping...', extensionFolder.resource.path) - continue - } const from = extensionFolder.resource const to = URI.joinPath(toParent, extensionFolder.name) const toStat = await fileService.resolve(from) if (toStat.isDirectory) { - await fileService.copy(from, to, true) + if (!isBlacklisted(extensionFolder.resource.fsPath)) { + await fileService.copy(from, to, true) + } } else if (toStat.isFile) { if (extensionFolder.name === 'extensions.json') { try { const contentsStr = await fileService.readFile(from) const json: any = JSON.parse(contentsStr.value.toString()) - const j2 = json.filter((entry: { identifier?: { id?: string } }) => - !extensionBlacklist.find(bItem => entry?.identifier?.id?.includes(bItem)) - ) + const j2 = json.filter((entry: { identifier?: { id?: string } }) => !isBlacklisted(entry?.identifier?.id)) const jsonStr = JSON.stringify(j2) await fileService.writeFile(to, VSBuffer.fromString(jsonStr)) } @@ -145,8 +144,7 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS try { if (child.isDirectory) { // if is blacklisted - if (extensionBlacklist.find(bItem => child.resource.path.includes(bItem))) { - + if (isBlacklisted(child.resource.fsPath)) { console.log('Deleting extension', child.resource.fsPath) await fileService.del(child.resource, { recursive: true, useTrash: true }) } @@ -157,9 +155,7 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS try { const contentsStr = await fileService.readFile(child.resource) const json: any = JSON.parse(contentsStr.value.toString()) - const j2 = json.filter((entry: { identifier?: { id?: string } }) => - !extensionBlacklist.find(bItem => entry?.identifier?.id?.includes(bItem)) - ) + const j2 = json.filter((entry: { identifier?: { id?: string } }) => !isBlacklisted(entry?.identifier?.id)) const jsonStr = JSON.stringify(j2) await fileService.writeFile(child.resource, VSBuffer.fromString(jsonStr)) } From c2b65eb16fcd04a25eb06251ca55457ab56e3cd3 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 12 May 2025 10:59:53 -0700 Subject: [PATCH 29/30] log --- .../workbench/contrib/void/browser/extensionTransferService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts index 39269a12..bbfa80ec 100644 --- a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts +++ b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts @@ -151,6 +151,8 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS } else if (child.isFile) { // if is extensions.json + console.log('Updating extensions.json', child.resource.fsPath) + if (child.name === 'extensions.json') { try { const contentsStr = await fileService.readFile(child.resource) From 24102fc2b267df3bb7c9041110287f5fd50306a5 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 12 May 2025 11:00:05 -0700 Subject: [PATCH 30/30] log2 --- .../workbench/contrib/void/browser/extensionTransferService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts index bbfa80ec..b8843e98 100644 --- a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts +++ b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts @@ -151,9 +151,9 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS } else if (child.isFile) { // if is extensions.json - console.log('Updating extensions.json', child.resource.fsPath) if (child.name === 'extensions.json') { + console.log('Updating extensions.json', child.resource.fsPath) try { const contentsStr = await fileService.readFile(child.resource) const json: any = JSON.parse(contentsStr.value.toString())