diff --git a/product.json b/product.json index a35f197c..d892feaf 100644 --- a/product.json +++ b/product.json @@ -31,6 +31,10 @@ "nodejsRepository": "https://nodejs.org", "urlProtocol": "code-oss", "webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-cdn.net/insider/ef65ac1ba57f57f2a3961bfe94aa20481caca4c6/out/vs/workbench/contrib/webview/browser/pre/", + "extensionsGallery": { + "serviceUrl": "https://open-vsx.org/vscode/gallery", + "itemUrl": "https://open-vsx.org/vscode/item" + }, "builtInExtensions": [ { "name": "ms-vscode.js-debug-companion", diff --git a/src/vs/workbench/contrib/void/browser/helpers/reactServicesHelper.ts b/src/vs/workbench/contrib/void/browser/helpers/reactServicesHelper.ts index 3c043344..08ad3fdb 100644 --- a/src/vs/workbench/contrib/void/browser/helpers/reactServicesHelper.ts +++ b/src/vs/workbench/contrib/void/browser/helpers/reactServicesHelper.ts @@ -9,10 +9,12 @@ import { ILLMMessageService } from '../../../../../platform/void/common/llmMessa import { IRefreshModelService } from '../../../../../platform/void/common/refreshModelService.js'; import { IVoidSettingsService } from '../../../../../platform/void/common/voidSettingsService.js'; import { IInlineDiffsService } from '../inlineDiffsService.js'; +import { IQuickEditStateService } from '../quickEditStateService.js'; import { ISidebarStateService } from '../sidebarStateService.js'; import { IThreadHistoryService } from '../threadHistoryService.js'; export type ReactServicesType = { + quickEditStateService: IQuickEditStateService; sidebarStateService: ISidebarStateService; settingsStateService: IVoidSettingsService; threadsStateService: IThreadHistoryService; @@ -33,6 +35,7 @@ export type ReactServicesType = { export const getReactServices = (accessor: ServicesAccessor): ReactServicesType => { return { + quickEditStateService: accessor.get(IQuickEditStateService), settingsStateService: accessor.get(IVoidSettingsService), sidebarStateService: accessor.get(ISidebarStateService), threadsStateService: accessor.get(IThreadHistoryService), diff --git a/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts index 5d6c0c49..e33c9991 100644 --- a/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts +++ b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts @@ -11,7 +11,7 @@ import { ICodeEditor, IOverlayWidget, IViewZone } from '../../../../editor/brows // import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; // import { throttle } from '../../../../base/common/decorators.js'; -import { writeFileWithDiffInstructions } from './prompt/systemPrompts.js'; +import { writeFileWithDiffInstructions } from './prompt/prompts.js'; import { ComputedDiff, findDiffs } from './helpers/findDiffs.js'; import { EndOfLinePreference, ITextModel } from '../../../../editor/common/model.js'; import { IRange } from '../../../../editor/common/core/range.js'; diff --git a/src/vs/workbench/contrib/void/browser/prompt/systemPrompts.ts b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts similarity index 71% rename from src/vs/workbench/contrib/void/browser/prompt/systemPrompts.ts rename to src/vs/workbench/contrib/void/browser/prompt/prompts.ts index 157f4292..803029c2 100644 --- a/src/vs/workbench/contrib/void/browser/prompt/systemPrompts.ts +++ b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts @@ -3,37 +3,144 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -// // used for ctrl+l -// const partialGenerationInstructions = `` + +import { CodeSelection } from '../threadHistoryService.js'; + +const stringifySelections = (selections: CodeSelection[]) => { + + return selections.map(({ fileURI, content, selectionStr }) => + `\ +File: ${fileURI.fsPath} +\`\`\` +${content // this was the enite file which is foolish + } +\`\`\`${selectionStr === null ? '' : ` +Selection: ${selectionStr}`} +`).join('\n') +} -// // used for ctrl+k, autocomplete -// const fimInstructions = `` +export const generateCtrlLPrompt = (instructions: string, selections: CodeSelection[] | null) => { + let str = ''; + if (selections && selections.length > 0) { + str += stringifySelections(selections); + str += `Please edit the selected code following these instructions:\n` + } + str += `${instructions}`; + return str; +}; -// CTRL+K prompt: -// const promptContent = `Here is the user's original selection: -// \`\`\` -// ${selection} -// \`\`\` -// The user wants to apply the following instructions to the selection: -// ${instructions} +export const ctrlLSystem = `\ +You are a coding assistant. You are given a list of relevant files \`files\`, a selection that the user is making \`selection\`, and instructions to follow \`instructions\`. -// Please rewrite the selection following the user's instructions. +Please edit the selected file following the user's instructions (or, if appropriate, answer their question instead). -// Instructions to follow: -// 1. Follow the user's instructions -// 2. You may ONLY CHANGE the selection, and nothing else in the file -// 3. Make sure all brackets in the new selection are balanced the same was as in the original selection -// 3. Be careful not to duplicate or remove variables, comments, or other syntax by mistake +Instructions: +1. Output the changes to make to the entire file. +1. Do not re-write the entire file. +3. Instead, you may use code elision to represent unchanged portions of code. For example, write "existing code..." in code comments. +4. You must give enough context to apply the change in the correct location. -// Complete the following: -// \`\`\` -//
${prefix}
-// ${suffix} -// `; +## EXAMPLE +FILES +selected file \`math.ts\`: +\`\`\` +const addNumbers = (a, b) => a + b +const subtractNumbers = (a, b) => a - b +const divideNumbers = (a, b) => a / b +\`\`\` + +SELECTION +\`\`\` +const subtractNumbers = (a, b) => a - b +\`\`\` + +INSTRUCTIONS +\`\`\` +add a function that multiplies numbers below this +\`\`\` + +EXPECTED OUTPUT +We can add the following code to the file: +\`\`\` +// existing code... +const subtractNumbers = (a, b) => a - b; +const multiplyNumbers = (a, b) => a * b; +// existing code... +\`\`\` + +## EXAMPLE + +FILES +selected file \`fib.ts\`: +\`\`\` + +const dfs = (root) => { + if (!root) return; + console.log(root.val); + dfs(root.left); + dfs(root.right); +} +const fib = (n) => { + if (n < 1) return 1 + return fib(n - 1) + fib(n - 2) +} +\`\`\` + +SELECTION +\`\`\` + return fib(n - 1) + fib(n - 2) +\`\`\` + +INSTRUCTIONS +\`\`\` +memoize results +\`\`\` + +EXPECTED OUTPUT +To implement memoization in your Fibonacci function, you can use a JavaScript object to store previously computed results. This will help avoid redundant calculations and improve performance. Here's how you can modify your function: +\`\`\` +// existing code... +const fib = (n, memo = {}) => { + if (n < 1) return 1; + if (memo[n]) return memo[n]; // Check if result is already computed + memo[n] = fib(n - 1, memo) + fib(n - 2, memo); // Store result in memo + return memo[n]; +} +\`\`\` +Explanation: +Memoization Object: A memo object is used to store the results of Fibonacci calculations for each n. +Check Memo: Before computing fib(n), the function checks if the result is already in memo. If it is, it returns the stored result. +Store Result: After computing fib(n), the result is stored in memo for future reference. + +## END EXAMPLES\ +` + +export const generateCtrlKPrompt = ({ selection, prefix, suffix, instructions, }: { selection: string, prefix: string, suffix: string, instructions: string, }) => `\ +Here is the user's original selection: +\`\`\` +${selection} +\`\`\` + +The user wants to apply the following instructions to the selection: +${instructions} + +Please rewrite the selection following the user's instructions. + +Instructions to follow: +1. Follow the user's instructions +2. You may ONLY CHANGE the selection, and nothing else in the file +3. Make sure all brackets in the new selection are balanced the same was as in the original selection +3. Be careful not to duplicate or remove variables, comments, or other syntax by mistake + +Complete the following: +\`\`\` +
${prefix}
+${suffix} +`; export const generateDiffInstructions = ` diff --git a/src/vs/workbench/contrib/void/browser/prompt/stringifySelections.ts b/src/vs/workbench/contrib/void/browser/prompt/stringifySelections.ts deleted file mode 100644 index e23ac6f4..00000000 --- a/src/vs/workbench/contrib/void/browser/prompt/stringifySelections.ts +++ /dev/null @@ -1,32 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Glass Devtools, Inc. All rights reserved. - * Void Editor additions licensed under the AGPL 3.0 License. - *--------------------------------------------------------------------------------------------*/ - -import { CodeSelection } from '../threadHistoryService.js'; - -export const stringifySelections = (selections: CodeSelection[]) => { - - - - return selections.map(({ fileURI, content, selectionStr }) => - `\ -File: ${fileURI.fsPath} -\`\`\` -${content // this was the enite file which is foolish - } -\`\`\`${selectionStr === null ? '' : ` -Selection: ${selectionStr}`} -`).join('\n') -} - - -export const userInstructionsStr = (instructions: string, selections: CodeSelection[] | null) => { - let str = ''; - if (selections && selections.length > 0) { - str += stringifySelections(selections); - str += `Please edit the selected code following these instructions:\n` - } - str += `${instructions}`; - return str; -}; diff --git a/src/vs/workbench/contrib/void/browser/quickEditActions.ts b/src/vs/workbench/contrib/void/browser/quickEditActions.ts index fcef7270..784011d2 100644 --- a/src/vs/workbench/contrib/void/browser/quickEditActions.ts +++ b/src/vs/workbench/contrib/void/browser/quickEditActions.ts @@ -1,9 +1,111 @@ import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; -import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; +import { ICodeEditor, IViewZone } from '../../../../editor/browser/editorBrowser.js'; import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; -import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { createDecorator, IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { IMetricsService } from '../../../../platform/void/common/metricsService.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +// import { IInlineDiffService } from '../../../../editor/browser/services/inlineDiffService/inlineDiffService.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; +import { mountCtrlK } from './react/out/ctrl-k-tsx/index.js'; +import { getReactServices } from './helpers/reactServicesHelper.js'; +import { URI } from '../../../../base/common/uri.js'; + + +type InitialZone = { uri: URI, startLine: number, selectedText: string, } + +export type QuickEditPropsType = { + quickEditId: number, +} + +export type QuickEdit = { + startLine: number, // 0-indexed + beforeCode: string, + afterCode?: string, + instructions?: string, + responseText?: string, // model can produce a text response too +} + + +export interface IQuickEditService { + readonly _serviceBrand: undefined; + readonly onDidChangeState: Event; + addZone(zone: InitialZone): void; +} + +export const IQuickEditService = createDecorator('voidQuickEditService'); +class VoidQuickEditService extends Disposable implements IQuickEditService { + _serviceBrand: undefined; + + quickEditId: number = 0 + + private readonly _onDidChangeState = new Emitter(); + readonly onDidChangeState: Event = this._onDidChangeState.event; + + // state + // state: {} + + constructor( + // @IInlineDiffService private readonly _inlineDiffService: IInlineDiffService, + @ICodeEditorService private readonly _editorService: ICodeEditorService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { + super(); + } + + addZone(zone: InitialZone) { + + const addZoneToEditor = (editor: ICodeEditor) => { + + const model = editor.getModel() + if (!model) return + + editor.changeViewZones(accessor => { + + const domNode = document.createElement('div'); + domNode.style.zIndex = '1' + + // domNode.className = 'void-redBG' + const viewZone: IViewZone = { + // afterLineNumber: computedDiff.startLine - 1, + afterLineNumber: 1, + heightInPx: 100, + // heightInLines: 1, + // minWidthInPx: 200, + domNode: domNode, + // marginDomNode: document.createElement('div'), // displayed to left + suppressMouseDown: false, + }; + + // const zoneId = + accessor.addZone(viewZone) + + this._instantiationService.invokeFunction(accessor => { + const services = getReactServices(accessor) + + const props: QuickEditPropsType = { + quickEditId: this.quickEditId++, + } + mountCtrlK(domNode, services, props) + }) + + // disposeInThisEditorFns.push(() => { editor.changeViewZones(accessor => { if (zoneId) accessor.removeZone(zoneId) }) }) + }) + } + + + const editors = this._editorService.listCodeEditors().filter(editor => editor.getModel()?.uri.fsPath === zone.uri.fsPath) + for (const editor of editors) { + addZoneToEditor(editor) + } + } + +} + +registerSingleton(IQuickEditService, VoidQuickEditService, InstantiationType.Eager); + export const VOID_CTRL_K_ACTION_ID = 'void.ctrlKAction' @@ -12,17 +114,25 @@ registerAction2(class extends Action2 { super({ id: VOID_CTRL_K_ACTION_ID, title: 'Void: Quick Edit', keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyK, weight: KeybindingWeight.BuiltinExtension } }); } async run(accessor: ServicesAccessor): Promise { - console.log('hello111!') - const model = accessor.get(ICodeEditorService).getActiveCodeEditor()?.getModel() - if (!model) - return - - console.log('hello!') + const quickEditService = accessor.get(IQuickEditService) + const editorService = accessor.get(ICodeEditorService) const metricsService = accessor.get(IMetricsService) - metricsService.capture('User Action', { type: 'Ctrl+K' }) + metricsService.capture('User Action', { type: 'Open Ctrl+K' }) + + const editor = editorService.getActiveCodeEditor() + if (!editor) return; + const model = editor.getModel() + if (!model) return; + const selection = editor.getSelection() + if (!selection) return; + + const uri = model.uri + const startLine = selection.startLineNumber + const selectedText = model.getValueInRange(selection) + + quickEditService.addZone({ uri, startLine, selectedText, }) - console.log('bye!') } }); diff --git a/src/vs/workbench/contrib/void/browser/quickEditStateService.ts b/src/vs/workbench/contrib/void/browser/quickEditStateService.ts new file mode 100644 index 00000000..355b52f1 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/quickEditStateService.ts @@ -0,0 +1,82 @@ +import { Emitter, Event } from '../../../../base/common/event.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { QuickEdit } from './quickEditActions.js'; + + + +// service that manages state +export type VoidQuickEditState = { + quickEditsOfDocument: { [uri: string]: QuickEdit } +} + +export interface IQuickEditStateService { + readonly _serviceBrand: undefined; + + readonly state: VoidQuickEditState; // readonly to the user + setState(newState: Partial): void; + onDidChangeState: Event; + + onDidFocusChat: Event; + onDidBlurChat: Event; + fireFocusChat(): void; + fireBlurChat(): void; + +} + +export const IQuickEditStateService = createDecorator('voidQuickEditStateService'); +class VoidQuickEditStateService extends Disposable implements IQuickEditStateService { + _serviceBrand: undefined; + + static readonly ID = 'voidQuickEditStateService'; + + private readonly _onDidChangeState = new Emitter(); + readonly onDidChangeState: Event = this._onDidChangeState.event; + + private readonly _onFocusChat = new Emitter(); + readonly onDidFocusChat: Event = this._onFocusChat.event; + + private readonly _onBlurChat = new Emitter(); + readonly onDidBlurChat: Event = this._onBlurChat.event; + + + // state + state: VoidQuickEditState + + constructor( + // @IViewsService private readonly _viewsService: IViewsService, + ) { + super() + + // initial state + this.state = { quickEditsOfDocument: {} } + } + + + setState(newState: Partial) { + // make sure view is open if the tab changes + // if ('currentTab' in newState) { + // this.addQuickEdit() + // } + + this.state = { ...this.state, ...newState } + this._onDidChangeState.fire() + } + + fireFocusChat() { + this._onFocusChat.fire() + } + + fireBlurChat() { + this._onBlurChat.fire() + } + + // addQuickEdit() { + // this._viewsService.openViewContainer(VOID_VIEW_CONTAINER_ID); + // this._viewsService.openView(VOID_VIEW_ID); + // } + +} + +registerSingleton(IQuickEditStateService, VoidQuickEditStateService, InstantiationType.Eager); diff --git a/src/vs/workbench/contrib/void/browser/react/src/ctrl-k-tsx/CtrlK.tsx b/src/vs/workbench/contrib/void/browser/react/src/ctrl-k-tsx/CtrlK.tsx new file mode 100644 index 00000000..e57acf4a --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/ctrl-k-tsx/CtrlK.tsx @@ -0,0 +1,18 @@ +import { useEffect, useState } from 'react' +import { useIsDark, useSidebarState } from '../util/services.js' +import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' +import { CtrlKChat } from './CtrlKChat.js' +import { QuickEditPropsType } from '../../../quickEditActions.js' + +export const CtrlK = (props: QuickEditPropsType) => { + + const isDark = useIsDark() + + return
+ + + +
+ + +} diff --git a/src/vs/workbench/contrib/void/browser/react/src/ctrl-k-tsx/CtrlKChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/ctrl-k-tsx/CtrlKChat.tsx new file mode 100644 index 00000000..edfb3664 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/ctrl-k-tsx/CtrlKChat.tsx @@ -0,0 +1,83 @@ + +import React, { FormEvent, useCallback, useRef, useState } from 'react'; +import { useSettingsState, useSidebarState, useThreadsState, useQuickEditState, useService } from '../util/services.js'; +import { OnError } from '../../../../../../../platform/void/common/llmMessageTypes.js'; +import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; +import { getCmdKey } from '../../../helpers/getCmdKey.js'; +import { VoidInputBox } from '../util/inputs.js'; +import { QuickEditPropsType } from '../../../quickEditActions.js'; + +export const CtrlKChat = (props: QuickEditPropsType) => { + + const inputBoxRef: React.MutableRefObject = useRef(null); + + // -- imported state -- + // const threadsStateService = useService('service') + // const sidebarState = useSidebarState() + + const quickEditState = useQuickEditState() + + + // -- local state -- + // state of chat + const [messageStream, setMessageStream] = useState(null) + const [isLoading, setIsLoading] = useState(false) + const latestRequestIdRef = useRef(null) + const [latestError, setLatestError] = useState[0] | null>(null) + + + // state of current message + const [instructions, setInstructions] = useState('') // the user's instructions + const onChangeText = useCallback((newStr: string) => { setInstructions(newStr) }, [setInstructions]) + const isDisabled = !instructions.trim() + + const onSubmit = useCallback((e: FormEvent) => { + // TODO + }, []) + + return
{ + if (e.key === 'Enter' && !e.shiftKey) { + onSubmit(e) + } + }} + onSubmit={(e) => { + console.log('submit!') + onSubmit(e) + }} + onClick={(e) => { + if (e.currentTarget === e.target) { + inputBoxRef.current?.focus() + } + }} + > +
+ + {/* text input */} + +
+ + +
+ + + +} diff --git a/src/vs/workbench/contrib/void/browser/react/src/ctrl-k-tsx/index.tsx b/src/vs/workbench/contrib/void/browser/react/src/ctrl-k-tsx/index.tsx new file mode 100644 index 00000000..3b4882d5 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/ctrl-k-tsx/index.tsx @@ -0,0 +1,8 @@ + +import { mountFnGenerator } from '../util/mountFnGenerator.js' +import { CtrlK } from './CtrlK.js' + + +export const mountCtrlK = mountFnGenerator(CtrlK) + + 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 d2e2125a..75cc5419 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 @@ -3,12 +3,10 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -import React, { FormEvent, Fragment, useCallback, useEffect, useRef, useState } from 'react'; +import React, { ButtonHTMLAttributes, FormEvent, FormHTMLAttributes, Fragment, useCallback, useEffect, useRef, useState } from 'react'; import { useSettingsState, useService, useSidebarState, useThreadsState } from '../util/services.js'; -import { generateDiffInstructions } from '../../../prompt/systemPrompts.js'; -import { userInstructionsStr } from '../../../prompt/stringifySelections.js'; import { ChatMessage, CodeSelection, CodeStagingSelection } from '../../../threadHistoryService.js'; import { BlockCode } from '../markdown/BlockCode.js'; @@ -23,6 +21,7 @@ import { getCmdKey } from '../../../helpers/getCmdKey.js' import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; import { VoidInputBox } from '../util/inputs.js'; import { ModelDropdown } from '../void-settings-tsx/ModelDropdown.js'; +import { ctrlLSystem, generateCtrlLPrompt } from '../../../prompt/prompts.js'; const IconX = ({ size, className = '' }: { size: number, className?: string }) => { @@ -85,6 +84,33 @@ const IconSquare = ({ size, className = '' }: { size: number, className?: string ); }; +type ButtonProps = ButtonHTMLAttributes +export const ButtonSubmit = ({ className, disabled, ...props }: ButtonProps & Required>) => { + return +} + +export const ButtonStop = ({ className, ...props }: ButtonHTMLAttributes) => { + + return +} + const ScrollToBottomContainer = ({ children, className, style }: { children: React.ReactNode, className?: string, style?: React.CSSProperties }) => { const [isAtBottom, setIsAtBottom] = useState(true); // Start at bottom @@ -277,6 +303,8 @@ export const SidebarChat = () => { const threadsState = useThreadsState() const threadsStateService = useService('threadsStateService') + const llmMessageService = useService('llmMessageService') + // ----- SIDEBAR CHAT state (local) ----- // state of chat @@ -286,7 +314,6 @@ export const SidebarChat = () => { const [latestError, setLatestError] = useState[0] | null>(null) - const llmMessageService = useService('llmMessageService') // state of current message const [instructions, setInstructions] = useState('') // the user's instructions @@ -325,11 +352,11 @@ export const SidebarChat = () => { // add system message to chat history - const systemPromptElt: ChatMessage = { role: 'system', content: generateDiffInstructions } + const systemPromptElt: ChatMessage = { role: 'system', content: ctrlLSystem } threadsStateService.addMessageToCurrentThread(systemPromptElt) // add user's message to chat history - const userHistoryElt: ChatMessage = { role: 'user', content: userInstructionsStr(instructions, selections), displayContent: instructions, selections: selections } + const userHistoryElt: ChatMessage = { role: 'user', content: generateCtrlLPrompt(instructions, selections), displayContent: instructions, selections: selections } threadsStateService.addMessageToCurrentThread(userHistoryElt) const currentThread = threadsStateService.getCurrentThread(threadsStateService.state) // the the instant state right now, don't wait for the React state @@ -474,14 +501,14 @@ export const SidebarChat = () => { {/* middle row */}
`@@[&_textarea]:!void-${style}`) // apply styles to ancestor input and textarea elements + // .map(style => `@@[&_textarea]:!void-${style}`) // apply styles to ancestor textarea elements // .join(' ') + // ` outline-none` // .split(' ') - // .map(style => `@@[&_div.monaco-inputbox]:!void-${style}`) // apply styles to ancestor input and textarea elements + // .map(style => `@@[&_div.monaco-inputbox]:!void-${style}`) // .join(' '); `@@[&_textarea]:!void-bg-transparent @@[&_textarea]:!void-outline-none @@[&_textarea]:!void-text-vscode-input-fg @@[&_textarea]:!void-min-h-[81px] @@[&_textarea]:!void-max-h-[500px] @@[&_div.monaco-inputbox]:!void-outline-none` } @@ -508,27 +535,14 @@ export const SidebarChat = () => { {/* submit / stop button */} {isLoading ? // stop button - + /> : // submit button (up arrow) - + /> }
diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx index d67932dd..b674e7d5 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx @@ -8,8 +8,7 @@ import * as ReactDOM from 'react-dom/client' import { _registerServices } from './services.js'; import { ReactServicesType } from '../../../helpers/reactServicesHelper.js'; - -export const mountFnGenerator = (Component: (params: any) => React.ReactNode) => (rootElement: HTMLElement, services: ReactServicesType) => { +export const mountFnGenerator = (Component: (params: any) => React.ReactNode) => (rootElement: HTMLElement, services: ReactServicesType, props?: any) => { if (typeof document === 'undefined') { console.error('index.tsx error: document was undefined') return @@ -19,7 +18,7 @@ export const mountFnGenerator = (Component: (params: any) => React.ReactNode) => const root = ReactDOM.createRoot(rootElement) - root.render(); // tailwind dark theme indicator + root.render(); // tailwind dark theme indicator return disposables } diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx index 754fac08..2cee2419 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx @@ -11,6 +11,7 @@ import { ReactServicesType } from '../../../helpers/reactServicesHelper.js' import { VoidSidebarState } from '../../../sidebarStateService.js' import { VoidSettingsState } from '../../../../../../../platform/void/common/voidSettingsService.js' import { ColorScheme } from '../../../../../../../platform/theme/common/theme.js' +import { VoidQuickEditState } from '../../../quickEditStateService.js' import { RefreshModelStateOfProvider } from '../../../../../../../platform/void/common/refreshModelService.js' @@ -20,6 +21,9 @@ let services: ReactServicesType // even if React hasn't mounted yet, the variables are always updated to the latest state. // React listens by adding a setState function to these listeners. +let quickEditState: VoidQuickEditState +const quickEditStateListeners: Set<(s: VoidQuickEditState) => void> = new Set() + let sidebarState: VoidSidebarState const sidebarStateListeners: Set<(s: VoidSidebarState) => void> = new Set() @@ -51,7 +55,15 @@ export const _registerServices = (services_: ReactServicesType) => { wasCalled = true services = services_ - const { sidebarStateService, settingsStateService, threadsStateService, refreshModelService, themeService } = services + const { sidebarStateService, quickEditStateService, settingsStateService, threadsStateService, refreshModelService, themeService, } = services + + quickEditState = quickEditStateService.state + disposables.push( + quickEditStateService.onDidChangeState(() => { + quickEditState = quickEditStateService.state + quickEditStateListeners.forEach(l => l(quickEditState)) + }) + ) sidebarState = sidebarStateService.state disposables.push( @@ -108,6 +120,16 @@ export const useService = (serviceName: T): // -- state of services -- +export const useQuickEditState = () => { + const [s, ss] = useState(quickEditState) + useEffect(() => { + ss(quickEditState) + quickEditStateListeners.add(ss) + return () => { quickEditStateListeners.delete(ss) } + }, [ss]) + return s +} + export const useSidebarState = () => { const [s, ss] = useState(sidebarState) useEffect(() => { 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 b3bb7368..2a7547a0 100644 --- a/src/vs/workbench/contrib/void/browser/react/tsup.config.js +++ b/src/vs/workbench/contrib/void/browser/react/tsup.config.js @@ -9,6 +9,7 @@ export default defineConfig({ entry: [ './src2/sidebar-tsx/index.tsx', './src2/void-settings-tsx/index.tsx', + './src2/ctrl-k-tsx/index.tsx', './src2/diff/index.tsx', ], outDir: './out',