mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
mostly works!! tracethroughs would be insanely helpful...
This commit is contained in:
parent
c5a6f476ef
commit
1d590510ae
3 changed files with 75 additions and 73 deletions
|
|
@ -32,7 +32,6 @@ import { ILLMMessageService } from '../../../../platform/void/common/llmMessageS
|
|||
|
||||
import { mountCtrlK } from '../browser/react/out/quick-edit-tsx/index.js'
|
||||
import { QuickEditPropsType } from './quickEditActions.js';
|
||||
import { InputBox } from '../../../../base/browser/ui/inputbox/inputBox.js';
|
||||
import { errorDetails, LLMMessage } from '../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { IModelContentChangedEvent } from '../../../../editor/common/textModelEvents.js';
|
||||
import { extractCodeFromFIM, extractCodeFromRegular } from './helpers/extractCodeFromResult.js';
|
||||
|
|
@ -49,7 +48,7 @@ import { isMacintosh } from '../../../../base/common/platform.js';
|
|||
const configOfBG = (color: Color) => {
|
||||
return { dark: color, light: color, hcDark: color, hcLight: color, }
|
||||
}
|
||||
// gets converted to --vscode-void-greenBG, see void.css
|
||||
// gets converted to --vscode-void-greenBG, see void.css, asCssVariable
|
||||
const greenBG = new Color(new RGBA(155, 185, 85, .3)); // default is RGBA(155, 185, 85, .2)
|
||||
registerColor('void.greenBG', configOfBG(greenBG), '', true);
|
||||
|
||||
|
|
@ -126,7 +125,7 @@ type CtrlKZone = {
|
|||
editorId: string; // the editor the input lives on
|
||||
|
||||
_mountInfo: null | {
|
||||
inputBoxRef: { current: InputBox | null }; // the input box that lives in the zone
|
||||
textAreaRef: { current: HTMLTextAreaElement | null }
|
||||
dispose: () => void;
|
||||
refresh: () => void;
|
||||
}
|
||||
|
|
@ -342,15 +341,16 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
|
||||
let zoneId: string | null = null
|
||||
let viewZone_: IViewZone | null = null
|
||||
const inputBoxRef: { current: InputBox | null } = { current: null }
|
||||
const textAreaRef: { current: HTMLTextAreaElement | null } = { current: null }
|
||||
|
||||
const itemId = this._consistentEditorItemService.addToEditor(editor, () => {
|
||||
const domNode = document.createElement('div');
|
||||
domNode.style.zIndex = '1'
|
||||
domNode.style.height = 'auto'
|
||||
const viewZone: IViewZone = {
|
||||
afterLineNumber: ctrlKZone.startLine - 1,
|
||||
domNode: domNode,
|
||||
heightInPx: 52,
|
||||
// heightInPx: 80,
|
||||
suppressMouseDown: false,
|
||||
showInHiddenAreas: true,
|
||||
};
|
||||
|
|
@ -361,30 +361,32 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
zoneId = accessor.addZone(viewZone)
|
||||
})
|
||||
|
||||
|
||||
// mount react
|
||||
this._instantiationService.invokeFunction(accessor => {
|
||||
mountCtrlK(domNode, accessor, {
|
||||
|
||||
diffareaid: ctrlKZone.diffareaid,
|
||||
onGetInputBox: (inputBox) => {
|
||||
inputBoxRef.current = inputBox
|
||||
// if it's mounting for the first time, focus it
|
||||
|
||||
textAreaRef: (r) => {
|
||||
textAreaRef.current = r
|
||||
if (!textAreaRef.current) return
|
||||
|
||||
if (!(ctrlKZone.diffareaid in this.mostRecentTextOfCtrlKZoneId)) { // detect first mount this way (a hack)
|
||||
this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] = undefined
|
||||
setTimeout(() => inputBox.focus(), 0)
|
||||
setTimeout(() => textAreaRef.current?.focus(), 100)
|
||||
}
|
||||
},
|
||||
onChangeHeight(height) {
|
||||
if (height === undefined) return
|
||||
if (height === 0) return // if hidden, height is set to 0 creating a jumpy scroll. ignore
|
||||
viewZone.heightInPx = height
|
||||
// re-render with this new height
|
||||
editor.changeViewZones(accessor => {
|
||||
if (zoneId) {
|
||||
accessor.layoutZone(zoneId)
|
||||
}
|
||||
if (zoneId) accessor.layoutZone(zoneId)
|
||||
})
|
||||
},
|
||||
onUserUpdateText: (text) => { this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] = text; },
|
||||
onChangeText: (text) => {
|
||||
this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] = text;
|
||||
},
|
||||
initText: this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] ?? null,
|
||||
} satisfies QuickEditPropsType)
|
||||
|
||||
|
|
@ -397,7 +399,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
})
|
||||
|
||||
return {
|
||||
inputBoxRef,
|
||||
textAreaRef,
|
||||
refresh: () => editor.changeViewZones(accessor => {
|
||||
if (zoneId && viewZone_) {
|
||||
viewZone_.afterLineNumber = ctrlKZone.startLine - 1
|
||||
|
|
@ -956,7 +958,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
// check if there's overlap with any other ctrlKZone and if so, focus it
|
||||
const overlappingCtrlKZone = this._findOverlappingDiffArea({ startLine, endLine, uri, filter: (diffArea) => diffArea.type === 'CtrlKZone' })
|
||||
if (overlappingCtrlKZone) {
|
||||
setTimeout(() => (overlappingCtrlKZone as CtrlKZone)._mountInfo?.inputBoxRef.current?.focus(), 0)
|
||||
(overlappingCtrlKZone as CtrlKZone)._mountInfo?.textAreaRef.current?.focus()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -1057,8 +1059,8 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
startLine = startLine_
|
||||
endLine = endLine_
|
||||
|
||||
if (!_mountInfo?.inputBoxRef.current) return
|
||||
userMessage = _mountInfo.inputBoxRef.current?.value
|
||||
if (!_mountInfo?.textAreaRef.current) return
|
||||
userMessage = _mountInfo.textAreaRef.current?.value
|
||||
}
|
||||
else {
|
||||
throw new Error(`Void: diff.type not recognized on: ${featureName}`)
|
||||
|
|
|
|||
|
|
@ -5,27 +5,29 @@
|
|||
|
||||
import React, { FormEvent, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useSettingsState, useSidebarState, useThreadsState, useQuickEditState, useAccessor } from '../util/services.js';
|
||||
import { OnError } from '../../../../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
|
||||
import { VoidInputBox } from '../util/inputs.js';
|
||||
import { TextAreaFns, VoidInputBox2 } from '../util/inputs.js';
|
||||
import { QuickEditPropsType } from '../../../quickEditActions.js';
|
||||
import { ButtonStop, ButtonSubmit, IconX } from '../sidebar-tsx/SidebarChat.js';
|
||||
import { ModelDropdown } from '../void-settings-tsx/ModelDropdown.js';
|
||||
import { X } from 'lucide-react';
|
||||
import { VOID_CTRL_K_ACTION_ID } from '../../../actionIDs.js';
|
||||
|
||||
export const QuickEditChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onChangeHeight, initText }: QuickEditPropsType) => {
|
||||
export const QuickEditChat = ({
|
||||
diffareaid,
|
||||
onChangeHeight,
|
||||
onChangeText: onChangeText_,
|
||||
textAreaRef: textAreaRef_,
|
||||
initText
|
||||
}: QuickEditPropsType) => {
|
||||
|
||||
const accessor = useAccessor()
|
||||
const inlineDiffsService = accessor.get('IInlineDiffsService')
|
||||
const sizerRef = useRef<HTMLDivElement | null>(null)
|
||||
const inputBoxRef: React.MutableRefObject<InputBox | null> = useRef(null);
|
||||
|
||||
const textAreaRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
const textAreaFnsRef = useRef<TextAreaFns | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const inputContainer = sizerRef.current
|
||||
if (!inputContainer) return;
|
||||
|
||||
// only observing 1 element
|
||||
let resizeObserver: ResizeObserver | undefined
|
||||
resizeObserver = new ResizeObserver((entries) => {
|
||||
|
|
@ -33,37 +35,35 @@ export const QuickEditChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onC
|
|||
onChangeHeight(height)
|
||||
})
|
||||
resizeObserver.observe(inputContainer);
|
||||
|
||||
return () => { resizeObserver?.disconnect(); };
|
||||
}, [onChangeHeight]);
|
||||
|
||||
|
||||
// state of current message
|
||||
const [instructions, setInstructions] = useState(initText ?? '') // the user's instructions
|
||||
const onChangeText = useCallback((newStr: string) => {
|
||||
setInstructions(newStr)
|
||||
onUserUpdateText(newStr)
|
||||
}, [setInstructions])
|
||||
const isDisabled = !instructions.trim()
|
||||
const [instructionsAreEmpty, setInstructionsAreEmpty] = useState(!(initText ?? '')) // the user's instructions
|
||||
const isDisabled = instructionsAreEmpty
|
||||
|
||||
const currentlyStreamingIdRef = useRef<number | undefined>(undefined)
|
||||
const [isStreaming, setIsStreaming] = useState(false)
|
||||
|
||||
const onSubmit = useCallback((e: FormEvent) => {
|
||||
if (isDisabled) return
|
||||
if (currentlyStreamingIdRef.current !== undefined) return
|
||||
inputBoxRef.current?.disable()
|
||||
textAreaFnsRef.current?.disable()
|
||||
|
||||
const instructions = textAreaRef.current?.value ?? ''
|
||||
currentlyStreamingIdRef.current = inlineDiffsService.startApplying({
|
||||
featureName: 'Ctrl+K',
|
||||
diffareaid: diffareaid,
|
||||
userMessage: instructions,
|
||||
})
|
||||
setIsStreaming(true)
|
||||
}, [inlineDiffsService, diffareaid, instructions])
|
||||
}, [isDisabled, inlineDiffsService, diffareaid])
|
||||
|
||||
const onInterrupt = useCallback(() => {
|
||||
if (currentlyStreamingIdRef.current !== undefined)
|
||||
inlineDiffsService.interruptStreaming(currentlyStreamingIdRef.current)
|
||||
inputBoxRef.current?.enable()
|
||||
textAreaFnsRef.current?.enable()
|
||||
setIsStreaming(false)
|
||||
}, [inlineDiffsService])
|
||||
|
||||
|
|
@ -73,19 +73,8 @@ export const QuickEditChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onC
|
|||
}, [inlineDiffsService, diffareaid])
|
||||
|
||||
|
||||
// sync init value
|
||||
const alreadySetRef = useRef(false)
|
||||
useEffect(() => {
|
||||
if (!inputBoxRef.current) return
|
||||
if (alreadySetRef.current) return
|
||||
alreadySetRef.current = true
|
||||
inputBoxRef.current.value = instructions
|
||||
}, [initText, instructions])
|
||||
|
||||
const keybindingString = accessor.get('IKeybindingService').lookupKeybinding(VOID_CTRL_K_ACTION_ID)?.getLabel()
|
||||
|
||||
|
||||
|
||||
return <div ref={sizerRef} className='py-2 w-full max-w-xl'>
|
||||
<form
|
||||
// copied from SidebarChat.tsx
|
||||
|
|
@ -96,20 +85,8 @@ export const QuickEditChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onC
|
|||
bg-vscode-input-bg
|
||||
border border-void-border-3 focus-within:border-void-border-1 hover:border-void-border-1
|
||||
`}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
onSubmit(e)
|
||||
return
|
||||
}
|
||||
}}
|
||||
onSubmit={(e) => {
|
||||
if (isDisabled)
|
||||
return
|
||||
console.log('submit!')
|
||||
onSubmit(e)
|
||||
}}
|
||||
onClick={(e) => {
|
||||
inputBoxRef.current?.focus()
|
||||
textAreaRef.current?.focus()
|
||||
}}
|
||||
>
|
||||
|
||||
|
|
@ -134,20 +111,38 @@ export const QuickEditChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onC
|
|||
`}
|
||||
>
|
||||
{/* text input */}
|
||||
<VoidInputBox
|
||||
placeholder={`${keybindingString} to select`}
|
||||
onChangeText={onChangeText}
|
||||
onCreateInstance={useCallback((instance: InputBox) => {
|
||||
inputBoxRef.current = instance;
|
||||
<VoidInputBox2
|
||||
|
||||
ref={useCallback((r: HTMLTextAreaElement | null) => {
|
||||
textAreaRef.current = r
|
||||
textAreaRef_(r)
|
||||
|
||||
// sync init value
|
||||
textAreaFnsRef.current?.setValue(initText ?? '')
|
||||
// if presses the esc key, X
|
||||
instance.element.addEventListener('keydown', (e) => {
|
||||
r?.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape')
|
||||
onX()
|
||||
})
|
||||
onGetInputBox(instance);
|
||||
instance.focus()
|
||||
}, [onGetInputBox])}
|
||||
|
||||
}, [textAreaRef_, initText, onX])}
|
||||
|
||||
fnsRef={textAreaFnsRef}
|
||||
|
||||
placeholder={`${keybindingString} to select`}
|
||||
|
||||
onChangeText={useCallback((newStr: string) => {
|
||||
setInstructionsAreEmpty(!newStr)
|
||||
onChangeText_(newStr)
|
||||
}, [onChangeText_])}
|
||||
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
onSubmit(e)
|
||||
return
|
||||
}
|
||||
}}
|
||||
|
||||
multiline={true}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -184,6 +179,7 @@ export const QuickEditChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onC
|
|||
:
|
||||
// submit button (up arrow)
|
||||
<ButtonSubmit
|
||||
onClick={onSubmit}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ type InputBox2Props = {
|
|||
fnsRef?: { current: null | TextAreaFns };
|
||||
onChangeText?: (value: string) => void;
|
||||
onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
||||
onChangeHeight?: (newHeight: number) => void;
|
||||
}
|
||||
export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(function X({ placeholder, multiline, fnsRef, onKeyDown, onChangeText }, ref) {
|
||||
|
||||
|
|
@ -62,9 +63,12 @@ export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(fun
|
|||
const adjustHeight = useCallback(() => {
|
||||
const r = textAreaRef.current
|
||||
if (!r) return
|
||||
r.style.height = 'auto';
|
||||
const newHeight = Math.min(r.scrollHeight + 1, 500);
|
||||
r.style.height = `${newHeight}px`;
|
||||
|
||||
r.style.height = 'auto' // set to auto to reset height, then set to new height
|
||||
if (r.scrollHeight === 0) return
|
||||
const h = r.scrollHeight
|
||||
const newHeight = Math.min(h, 500)
|
||||
r.style.height = `${newHeight}px`
|
||||
}, []);
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue