mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
draft
This commit is contained in:
parent
5d0a4c8157
commit
6bb0966eea
5 changed files with 232 additions and 181 deletions
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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<Record<string, number | undefined>>({})
|
||||
// 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 <div
|
||||
className={`bg-red-500 m-4 p-4 min-h-4 min-w-4 flex gap-4 ${isFocused ? 'ring-2 ring-[var(--vscode-focusBorder)]' : ''}`}
|
||||
onFocusCapture={() => setIsFocused(true)}
|
||||
onBlurCapture={() => setIsFocused(false)}
|
||||
>
|
||||
<button
|
||||
disabled={!getNextDiff({ step: -1 })}
|
||||
onClick={() => gotoNextDiff({ step: -1 })}
|
||||
>↑</button>
|
||||
|
||||
<button
|
||||
disabled={!getNextDiff({ step: 1 })}
|
||||
onClick={() => gotoNextDiff({ step: 1 })}
|
||||
>↓</button>
|
||||
|
||||
<button
|
||||
disabled={!getNextUri({ step: -1 })}
|
||||
onClick={() => gotoNextUri({ step: -1 })}
|
||||
>←</button>
|
||||
|
||||
<button
|
||||
disabled={!getNextUri({ step: 1 })}
|
||||
onClick={() => gotoNextUri({ step: 1 })}
|
||||
>→</button>
|
||||
|
||||
<div>
|
||||
<div className='gap-2 text-[var(--vscode-editor-foreground)] flex'>
|
||||
<div>File: {(editCodeService._sortedUrisWithDiffs.findIndex(u => u.fsPath === getCurrentUri()?.fsPath) ?? 0) + 1}/{editCodeService._sortedUrisWithDiffs.length}</div>
|
||||
<div>Diff: {(diffIdxOfFspath[getCurrentUri()?.fsPath ?? ''] ?? 0) + 1}/{editCodeService._sortedDiffsOfFspath[getCurrentUri()?.fsPath ?? '']?.length ?? 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{diffZones.map((area, index) => (
|
||||
<>
|
||||
<div key={index} className='bg-red-500 p-2 rounded-lg m-2 text-white'>{getBasename(area?._URI?.toString())}</div>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
|
||||
export const SidebarChat = () => {
|
||||
const textAreaRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
const textAreaFnsRef = useRef<TextAreaFns | null>(null)
|
||||
|
|
@ -2051,7 +1874,6 @@ export const SidebarChat = () => {
|
|||
/>
|
||||
|
||||
</VoidChatArea>
|
||||
<VoidCommandBar />
|
||||
</div>
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -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 <div
|
||||
className={`@@void-scope ${isDark ? 'dark' : ''}`}
|
||||
>
|
||||
<VoidCommandBar />
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
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<Record<string, number | undefined>>({})
|
||||
// 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 <div
|
||||
className={`flex items-center gap-2 p-2 ${isFocused ? 'ring-1 ring-[var(--vscode-focusBorder)]' : ''}`}
|
||||
onFocusCapture={() => setIsFocused(true)}
|
||||
onBlurCapture={() => setIsFocused(false)}
|
||||
>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
className={`px-2 py-1 rounded hover:bg-[var(--vscode-button-hoverBackground)] ${!getNextDiff({ step: -1 }) ? 'opacity-50' : ''}`}
|
||||
disabled={!getNextDiff({ step: -1 })}
|
||||
onClick={() => gotoNextDiff({ step: -1 })}
|
||||
title="Previous diff"
|
||||
>↑</button>
|
||||
|
||||
<button
|
||||
className={`px-2 py-1 rounded hover:bg-[var(--vscode-button-hoverBackground)] ${!getNextDiff({ step: 1 }) ? 'opacity-50' : ''}`}
|
||||
disabled={!getNextDiff({ step: 1 })}
|
||||
onClick={() => gotoNextDiff({ step: 1 })}
|
||||
title="Next diff"
|
||||
>↓</button>
|
||||
|
||||
<button
|
||||
className={`px-2 py-1 rounded hover:bg-[var(--vscode-button-hoverBackground)] ${!getNextUri({ step: -1 }) ? 'opacity-50' : ''}`}
|
||||
disabled={!getNextUri({ step: -1 })}
|
||||
onClick={() => gotoNextUri({ step: -1 })}
|
||||
title="Previous file"
|
||||
>←</button>
|
||||
|
||||
<button
|
||||
className={`px-2 py-1 rounded hover:bg-[var(--vscode-button-hoverBackground)] ${!getNextUri({ step: 1 }) ? 'opacity-50' : ''}`}
|
||||
disabled={!getNextUri({ step: 1 })}
|
||||
onClick={() => gotoNextUri({ step: 1 })}
|
||||
title="Next file"
|
||||
>→</button>
|
||||
</div>
|
||||
|
||||
<div className="text-[var(--vscode-editor-foreground)] text-xs flex gap-4">
|
||||
<div>File {(editCodeService._sortedUrisWithDiffs.findIndex(u => u.fsPath === getCurrentUri()?.fsPath) ?? 0) + 1} of {editCodeService._sortedUrisWithDiffs.length}</div>
|
||||
<div>Diff {(diffIdxOfFspath[getCurrentUri()?.fsPath ?? ''] ?? 0) + 1} of {editCodeService._sortedDiffsOfFspath[getCurrentUri()?.fsPath ?? '']?.length ?? 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Reference in a new issue