diff --git a/src/vs/workbench/contrib/void/browser/prompt/stringifyFiles.ts b/src/vs/workbench/contrib/void/browser/prompt/stringifySelections.ts similarity index 82% rename from src/vs/workbench/contrib/void/browser/prompt/stringifyFiles.ts rename to src/vs/workbench/contrib/void/browser/prompt/stringifySelections.ts index 2dd2f671..17372ec9 100644 --- a/src/vs/workbench/contrib/void/browser/prompt/stringifyFiles.ts +++ b/src/vs/workbench/contrib/void/browser/prompt/stringifySelections.ts @@ -5,13 +5,16 @@ import { CodeSelection } from '../registerThreads.js'; -export const filesStr = (selections: CodeSelection[]) => { +export const stringifySelections = (selections: CodeSelection[]) => { + + return selections.map(({ fileURI, content, selectionStr }) => `\ File: ${fileURI.fsPath} \`\`\` -${content} +${content // this was the enite file which is foolish + } \`\`\`${selectionStr === null ? '' : ` Selection: ${selectionStr}`} `).join('\n') @@ -21,7 +24,7 @@ Selection: ${selectionStr}`} export const userInstructionsStr = (instructions: string, selections: CodeSelection[] | null) => { let str = ''; if (selections && selections.length > 0) { - str += filesStr(selections); + str += stringifySelections(selections); str += `Please edit the selected code following these instructions:\n` } str += `${instructions}`; 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 ca855ab8..2f432f64 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 @@ -6,9 +6,9 @@ import React, { FormEvent, Fragment, useCallback, useEffect, useRef, useState } from 'react'; -import { useConfigState, useService, useThreadsState } from '../util/services.js'; +import { useConfigState, useService, useSidebarState, useThreadsState } from '../util/services.js'; import { generateDiffInstructions } from '../../../prompt/systemPrompts.js'; -import { userInstructionsStr } from '../../../prompt/stringifyFiles.js'; +import { userInstructionsStr } from '../../../prompt/stringifySelections.js'; import { ChatMessage, CodeSelection, CodeStagingSelection } from '../../../registerThreads.js'; import { BlockCode } from '../markdown/BlockCode.js'; @@ -23,6 +23,68 @@ import { getCmdKey } from '../../../getCmdKey.js' import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; import { VoidInputBox } from './inputs.js'; + +const IconX = ({ size, className = '' }: { size: number, className?: string }) => { + return ( + + + + ); +}; + + +const IconArrowUp = ({ size, className = '' }: { size: number, className?: string }) => { + return ( + + + + + ); +}; + + +const IconSquare = ({ size, className = '' }: { size: number, className?: string }) => { + return ( + + + + ); +}; + + // read files from VSCode const VSReadFile = async (modelService: IModelService, uri: URI): Promise => { const model = modelService.getModel(uri) @@ -43,51 +105,71 @@ export const SelectedFiles = ( | { type: 'past', selections: CodeSelection[] | null; setStaging?: undefined } | { type: 'staging', selections: CodeStagingSelection[] | null; setStaging: ((files: CodeStagingSelection[]) => void) } ) => { + + // index -> isOpened + const [selectionIsOpened, setSelectionIsOpened] = useState<(boolean)[]>(selections?.map(() => false) ?? []) + return ( !!selections && selections.length !== 0 && ( -
+
{selections.map((selection, i) => ( - - - {/* selection text */} - {type === 'staging' && selection.selectionStr && { - setStaging([...selections.slice(0, i), { ...selection, selectionStr: null }, ...selections.slice(i + 1, Infinity)]) - }} - className="btn btn-secondary btn-sm border border-vscode-input-border rounded" - >Remove - )} />} + + + } +
+ {/* selection full text */} + {type === 'staging' && selection.selectionStr && selectionIsOpened[i] && + { // clear the selection string but keep the file + // // setStaging([...selections.slice(0, i), { ...selection, selectionStr: null }, ...selections.slice(i + 1, Infinity)]) + // // }} + // onClick={() => { + // if (type !== 'staging') return + // setStaging([...selections.slice(0, i), ...selections.slice(i + 1, Infinity)]) + // }} + // className="btn btn-secondary btn-sm border border-vscode-input-border rounded" + // >Remove + // )} + /> + } - ))} + )) + }
) ) @@ -99,9 +181,8 @@ const ChatBubble = ({ chatMessage }: { }) => { const role = chatMessage.role - const children = chatMessage.displayContent - if (!children) + if (!chatMessage.displayContent) return null let chatbubbleContents: React.ReactNode @@ -109,11 +190,11 @@ const ChatBubble = ({ chatMessage }: { if (role === 'user') { chatbubbleContents = <> - {children} + {chatMessage.displayContent} } else if (role === 'assistant') { - chatbubbleContents = // sectionsHTML + chatbubbleContents = // sectionsHTML } return
@@ -146,7 +227,6 @@ export const SidebarChat = () => { // config state const voidConfigState = useConfigState() - // threads state const threadsState = useThreadsState() const threadsStateService = useService('threadsStateService') @@ -176,19 +256,33 @@ export const SidebarChat = () => { if (isLoading) return - const currSelns = threadsStateService.state._currentStagingSelections + + const currSelns = threadsStateService.state._currentStagingSelections ?? [] const selections = !currSelns ? null : await Promise.all( currSelns.map(async (sel) => ({ ...sel, content: await VSReadFile(modelService, sel.fileURI) })) ).then( (files) => files.filter(file => file.content !== null) as CodeSelection[] ) + + // // TODO don't save files to the thread history + // const selectedSnippets = currSelns.filter(sel => sel.selectionStr !== null) + // const selectedFiles = await Promise.all( // do not add these to the context history + // currSelns.filter(sel => sel.selectionStr === null) + // .map(async (sel) => ({ ...sel, content: await VSReadFile(modelService, sel.fileURI) })) + // ).then( + // (files) => files.filter(file => file.content !== null) as CodeSelection[] + // ) + // const contextToSendToLLM = '' + // const contextToAddToHistory = '' + + // add system message to chat history const systemPromptElt: ChatMessage = { role: 'system', content: generateDiffInstructions } threadsStateService.addMessageToCurrentThread(systemPromptElt) - const userContent = userInstructionsStr(instructions, selections) - const userHistoryElt: ChatMessage = { role: 'user', content: userContent, displayContent: instructions, selections } + // add user's message to chat history + const userHistoryElt: ChatMessage = { role: 'user', content: userInstructionsStr(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 @@ -239,7 +333,7 @@ export const SidebarChat = () => { } const onAbort = () => { - // abort the LLM + // abort the LLM call if (latestRequestIdRef.current) sendLLMMessageService.abort(latestRequestIdRef.current) @@ -264,87 +358,84 @@ export const SidebarChat = () => { {currentThread !== null && currentThread?.messages.map((message, i) => )} + {/* message stream */}
- {/* chatbar */} -
- {/* selection */} -
-
-
- {/* selections */} - {(selections && selections.length !== 0) &&
- -
} - {/* error message */} - {latestError === null ? null : - { setLatestError(null) }} - showDismiss={true} - />} + {/* input box */} +
{ if (e.key === 'Enter' && !e.shiftKey) onSubmit(e) }} - { if (e.key === 'Enter' && !e.shiftKey) onSubmit(e) }} + onSubmit={(e) => { + console.log('submit!') + onSubmit(e) + }} + > + {/* top row */} +
+ {/* selections */} + {(selections && selections.length !== 0) && + + } - onSubmit={(e) => { - console.log('submit!') - onSubmit(e) - }}> - - {/* input */} - - - {/*