mostly works!! tracethroughs would be insanely helpful...

This commit is contained in:
Andrew Pareles 2025-01-13 03:55:18 -08:00
parent c5a6f476ef
commit 1d590510ae
3 changed files with 75 additions and 73 deletions

View file

@ -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}`)

View file

@ -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}
/>
}

View file

@ -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`
}, []);