diff --git a/src/vs/workbench/contrib/void/browser/editCodeService.ts b/src/vs/workbench/contrib/void/browser/editCodeService.ts index 69a7e41d..68f7e0af 100644 --- a/src/vs/workbench/contrib/void/browser/editCodeService.ts +++ b/src/vs/workbench/contrib/void/browser/editCodeService.ts @@ -28,6 +28,7 @@ import { IConsistentEditorItemService, IConsistentItemService } from './helperSe import { voidPrefixAndSuffix, ctrlKStream_userMessage, ctrlKStream_systemMessage, defaultQuickEditFimTags, rewriteCode_systemMessage, rewriteCode_userMessage, searchReplace_systemMessage, searchReplace_userMessage, } from '../common/prompt/prompts.js'; import { mountCtrlK } from './react/out/quick-edit-tsx/index.js' +import { mountVoidCommandBar } from './react/out/void-command-bar-tsx/index.js' import { QuickEditPropsType } from './quickEditActions.js'; import { IModelContentChangedEvent } from '../../../../editor/common/textModelEvents.js'; import { extractCodeFromFIM, extractCodeFromRegular, ExtractedSearchReplaceBlock, extractSearchReplaceBlocks } from '../common/helpers/extractCodeFromResult.js'; @@ -331,7 +332,6 @@ class EditCodeService extends Disposable implements IEditCodeService { })) this._register(this._onDidChangeDiffZoneStreaming.event(({ uri: uri_ }) => { if (uri_.fsPath === model.uri.fsPath) updateAcceptRejectAllUI() })) this._register(this._onDidAddOrDeleteDiffZones.event(({ uri: uri_ }) => { if (uri_.fsPath === model.uri.fsPath) updateAcceptRejectAllUI() })) - // when the model first mounts, refresh any diffs that might be on it (happens if diffs were added in the BG) this._refreshStylesAndDiffsInURI(model.uri) } @@ -520,6 +520,7 @@ class EditCodeService extends Disposable implements IEditCodeService { this.removeDiffAreas({ uri, behavior: 'reject', removeCtrlKs: false }) this._metricsService.capture('Reject All', {}) }, + instantiationService: this._instantiationService, }) return () => { buttonsWidget.dispose() } } @@ -2351,15 +2352,23 @@ class AcceptAllRejectAllWidget extends Widget implements IOverlayWidget { private readonly _domNode: HTMLElement; private readonly editor: ICodeEditor; private readonly ID: string; + private readonly _instantiationService: IInstantiationService; - constructor({ editor, onAcceptAll, onRejectAll }: { editor: ICodeEditor, onAcceptAll: () => void, onRejectAll: () => void }) { + constructor({ editor, onAcceptAll, onRejectAll, instantiationService }: { + editor: ICodeEditor, + onAcceptAll: () => void, + onRejectAll: () => void, + instantiationService: IInstantiationService + }) { super(); this.ID = editor.getModel()?.uri.fsPath + ''; this.editor = editor; + this._instantiationService = instantiationService; // Create container div with buttons - const { acceptButton, rejectButton, buttons } = dom.h('div@buttons', [ + const { voidCommandBar, acceptButton, rejectButton, buttons } = dom.h('div@buttons', [ + dom.h('div@voidCommandBar', []), dom.h('button@acceptButton', []), dom.h('button@rejectButton', []) ]); @@ -2371,6 +2380,11 @@ class AcceptAllRejectAllWidget extends Widget implements IOverlayWidget { buttons.style.gap = '4px'; buttons.style.alignItems = 'center'; + // Mount command bar using mountVoidCommandBar + this._instantiationService.invokeFunction(accessor => { + mountVoidCommandBar(voidCommandBar, accessor, {}) + }); + // Style accept button acceptButton.addEventListener('click', onAcceptAll) acceptButton.textContent = 'Accept All'; 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 5744da5e..e84f563e 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 @@ -1677,183 +1677,6 @@ const ChatBubble = ({ chatMessage, isCommitted, messageIdx, isLast }: ChatBubble } - -const VoidCommandBar = () => { - const accessor = useAccessor() - const editCodeService = accessor.get('IEditCodeService') - const editorService = accessor.get('ICodeEditorService') - const commandService = accessor.get('ICommandService') - - const [_, rerender] = useState(0) - // Add a state variable to track focus - const [isFocused, setIsFocused] = useState(false) - - // state for what the user is currently focused on (both URI and diff) - const [diffIdxOfFspath, setDiffIdxOfFspath] = useState>({}) - // const [currentUriIdx, setCurrentUriIdx] = useState(-1) // we are doing O(n) search for this - - const getCurrentUri = useCallback(() => { - const editor = editorService.getActiveCodeEditor() - if (!editor) return null - const uri = editor.getModel()?.uri - if (!uri) return null - return uri - }, [editorService]) - - const diffZones: DiffZone[] = [] - - - // trigger rerender when diffzone is created (TODO need to also update when diff is accepted/rejected) - useEffect(() => { - const disposable = editCodeService.onDidAddOrDeleteDiffInDiffZone(() => { - rerender(c => c + 1) // rerender - }) - return () => disposable.dispose() - }, [editCodeService, rerender]) - - - const getNextDiff = useCallback(({ step }: { step: 1 | -1 }) => { - - const currentUri = getCurrentUri() - - if (!currentUri) { - return; - } - - const sortedDiffs = editCodeService._sortedDiffsOfFspath[currentUri.fsPath] - - if (!sortedDiffs || sortedDiffs.length === 0) { - return; - } - - const currentDiffIdx = diffIdxOfFspath[currentUri.fsPath] || 0 - const nextDiffIdx = (currentDiffIdx + step) % sortedDiffs.length - - const nextDiff = sortedDiffs[nextDiffIdx] - - return { nextDiff, nextDiffIdx, } - - }, [getCurrentUri, editCodeService._sortedDiffsOfFspath, diffIdxOfFspath]) - - const getNextUri = useCallback(({ step }: { step: 1 | -1 }) => { - - const sortedUris = editCodeService._sortedUrisWithDiffs - if (sortedUris.length === 0) { - return; - } - - const currentUri = getCurrentUri() - - const defaultUriIdx = step === 1 ? -1 : 0 // defaults: if next, currentIdx = -1; if prev, currentIdx = 0 - let currentUriIdx = -1 - if (currentUri) { - currentUriIdx = sortedUris.findIndex(u => u.fsPath === currentUri.fsPath) - } - - if (currentUriIdx === -1) { // not found - currentUriIdx = defaultUriIdx // set to default - } - - const nextUriIdx = (currentUriIdx + step) % sortedUris.length - const nextUri = sortedUris[nextUriIdx] - - return { nextUri, nextUriIdx, } - - }, [getCurrentUri, editCodeService._sortedUrisWithDiffs]) - - - const gotoNextDiff = ({ step }: { step: 1 | -1 }) => { - - // get the next diff - const res = getNextDiff({ step: 1 }) - if (!res) return; - - // scroll to the next diff - const { nextDiff, nextDiffIdx } = res; - const editor = editorService.getActiveCodeEditor() - if (!editor) return; - - const range = { startLineNumber: nextDiff.startLine, endLineNumber: nextDiff.startLine, startColumn: 1, endColumn: 1 }; - editor.revealRange(range, ScrollType.Immediate) - - // update state - const diffArea = editCodeService.diffAreaOfId[nextDiff.diffareaid] - setDiffIdxOfFspath(v => ({ ...v, [diffArea._URI.fsPath]: nextDiffIdx })) - - } - - const gotoNextUri = ({ step }: { step: 1 | -1 }) => { - - // get the next uri - const res = getNextUri({ step: 1 }) - if (!res) return; - - const { nextUri, nextUriIdx } = res; - - // open the uri and scroll to diff - const sortedDiffs = editCodeService._sortedDiffsOfFspath[nextUri.fsPath] - if (!sortedDiffs) return; - - const diffIdx = diffIdxOfFspath[nextUri.fsPath] || 0 - const diff = sortedDiffs[diffIdx] - - const range = { startLineNumber: diff.startLine, endLineNumber: diff.startLine, startColumn: 1, endColumn: 1 }; - - commandService.executeCommand('vscode.open', nextUri).then(() => { - - // select the text - setTimeout(() => { - - const editor = editorService.getActiveCodeEditor() - if (!editor) return; - - editor.revealRange(range, ScrollType.Immediate) - - }, 50) - - }) - } - - return
setIsFocused(true)} - onBlurCapture={() => setIsFocused(false)} - > - - - - - - - - -
-
-
File: {(editCodeService._sortedUrisWithDiffs.findIndex(u => u.fsPath === getCurrentUri()?.fsPath) ?? 0) + 1}/{editCodeService._sortedUrisWithDiffs.length}
-
Diff: {(diffIdxOfFspath[getCurrentUri()?.fsPath ?? ''] ?? 0) + 1}/{editCodeService._sortedDiffsOfFspath[getCurrentUri()?.fsPath ?? '']?.length ?? 0}
-
-
- - {diffZones.map((area, index) => ( - <> -
{getBasename(area?._URI?.toString())}
- - ))} -
-} - export const SidebarChat = () => { const textAreaRef = useRef(null) const textAreaFnsRef = useRef(null) @@ -2051,7 +1874,6 @@ export const SidebarChat = () => { /> - return ( diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx new file mode 100644 index 00000000..9f494909 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx @@ -0,0 +1,205 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + + +import { useAccessor, useIsDark } from '../util/services.js'; + +import '../styles.css' +import { DiffZone } from '../../../editCodeService.js'; +import { useCallback, useEffect, useState } from 'react'; +import { ScrollType } from '../../../../../../../editor/common/editorCommon.js'; +import { getBasename } from '../sidebar-tsx/SidebarChat.js'; + +export const VoidCommandBarMain = ({ className }: { className: string }) => { + const isDark = useIsDark() + + return
+ +
+} + + + +const VoidCommandBar = () => { + const accessor = useAccessor() + const editCodeService = accessor.get('IEditCodeService') + const editorService = accessor.get('ICodeEditorService') + const commandService = accessor.get('ICommandService') + + const [_, rerender] = useState(0) + // Add a state variable to track focus + const [isFocused, setIsFocused] = useState(false) + console.log('rerender count: ', _) + + // state for what the user is currently focused on (both URI and diff) + const [diffIdxOfFspath, setDiffIdxOfFspath] = useState>({}) + // const [currentUriIdx, setCurrentUriIdx] = useState(-1) // we are doing O(n) search for this + + const getCurrentUri = useCallback(() => { + const editor = editorService.getActiveCodeEditor() + if (!editor) return null + const uri = editor.getModel()?.uri + if (!uri) return null + return uri + }, [editorService]) + + const diffZones: DiffZone[] = [] + + + // trigger rerender when diffzone is created (TODO need to also update when diff is accepted/rejected) + useEffect(() => { + const disposable = editCodeService.onDidAddOrDeleteDiffInDiffZone(() => { + rerender(c => c + 1) // rerender + }) + return () => disposable.dispose() + }, [editCodeService, rerender]) + + + const getNextDiff = useCallback(({ step }: { step: 1 | -1 }) => { + + const currentUri = getCurrentUri() + + if (!currentUri) { + return; + } + + const sortedDiffs = editCodeService._sortedDiffsOfFspath[currentUri.fsPath] + + if (!sortedDiffs || sortedDiffs.length === 0) { + return; + } + + const currentDiffIdx = diffIdxOfFspath[currentUri.fsPath] || 0 + const nextDiffIdx = (currentDiffIdx + step) % sortedDiffs.length + + const nextDiff = sortedDiffs[nextDiffIdx] + + return { nextDiff, nextDiffIdx, } + + }, [getCurrentUri, editCodeService._sortedDiffsOfFspath, diffIdxOfFspath]) + + const getNextUri = useCallback(({ step }: { step: 1 | -1 }) => { + + const sortedUris = editCodeService._sortedUrisWithDiffs + if (sortedUris.length === 0) { + return; + } + + const currentUri = getCurrentUri() + + const defaultUriIdx = step === 1 ? -1 : 0 // defaults: if next, currentIdx = -1; if prev, currentIdx = 0 + let currentUriIdx = -1 + if (currentUri) { + currentUriIdx = sortedUris.findIndex(u => u.fsPath === currentUri.fsPath) + } + + if (currentUriIdx === -1) { // not found + currentUriIdx = defaultUriIdx // set to default + } + + const nextUriIdx = (currentUriIdx + step) % sortedUris.length + const nextUri = sortedUris[nextUriIdx] + + return { nextUri, nextUriIdx, } + + }, [getCurrentUri, editCodeService._sortedUrisWithDiffs]) + + + const gotoNextDiff = ({ step }: { step: 1 | -1 }) => { + + // get the next diff + const res = getNextDiff({ step: 1 }) + if (!res) return; + + // scroll to the next diff + const { nextDiff, nextDiffIdx } = res; + const editor = editorService.getActiveCodeEditor() + if (!editor) return; + + const range = { startLineNumber: nextDiff.startLine, endLineNumber: nextDiff.startLine, startColumn: 1, endColumn: 1 }; + editor.revealRange(range, ScrollType.Immediate) + + // update state + const diffArea = editCodeService.diffAreaOfId[nextDiff.diffareaid] + setDiffIdxOfFspath(v => ({ ...v, [diffArea._URI.fsPath]: nextDiffIdx })) + + } + + const gotoNextUri = ({ step }: { step: 1 | -1 }) => { + + // get the next uri + const res = getNextUri({ step: 1 }) + if (!res) return; + + const { nextUri, nextUriIdx } = res; + + // open the uri and scroll to diff + const sortedDiffs = editCodeService._sortedDiffsOfFspath[nextUri.fsPath] + if (!sortedDiffs) return; + + const diffIdx = diffIdxOfFspath[nextUri.fsPath] || 0 + const diff = sortedDiffs[diffIdx] + + const range = { startLineNumber: diff.startLine, endLineNumber: diff.startLine, startColumn: 1, endColumn: 1 }; + + commandService.executeCommand('vscode.open', nextUri).then(() => { + + // select the text + setTimeout(() => { + + const editor = editorService.getActiveCodeEditor() + if (!editor) return; + + editor.revealRange(range, ScrollType.Immediate) + + }, 50) + + }) + } + + return
setIsFocused(true)} + onBlurCapture={() => setIsFocused(false)} + > +
+ + + + + + + +
+ +
+
File {(editCodeService._sortedUrisWithDiffs.findIndex(u => u.fsPath === getCurrentUri()?.fsPath) ?? 0) + 1} of {editCodeService._sortedUrisWithDiffs.length}
+
Diff {(diffIdxOfFspath[getCurrentUri()?.fsPath ?? ''] ?? 0) + 1} of {editCodeService._sortedDiffsOfFspath[getCurrentUri()?.fsPath ?? '']?.length ?? 0}
+
+
+ +} diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/index.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/index.tsx new file mode 100644 index 00000000..5b185788 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/index.tsx @@ -0,0 +1,9 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { mountFnGenerator } from '../util/mountFnGenerator.js' +import { VoidCommandBarMain } from './VoidCommandBar.js' + +export const mountVoidCommandBar = mountFnGenerator(VoidCommandBarMain) diff --git a/src/vs/workbench/contrib/void/browser/react/tsup.config.js b/src/vs/workbench/contrib/void/browser/react/tsup.config.js index e51218e5..ab3bd525 100644 --- a/src/vs/workbench/contrib/void/browser/react/tsup.config.js +++ b/src/vs/workbench/contrib/void/browser/react/tsup.config.js @@ -7,6 +7,7 @@ import { defineConfig } from 'tsup' export default defineConfig({ entry: [ + './src2/void-command-bar-tsx/index.tsx', './src2/sidebar-tsx/index.tsx', './src2/void-settings-tsx/index.tsx', './src2/quick-edit-tsx/index.tsx',