From bf2843b1bd54d5daaed946d14364ec70f8607185 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 15 Mar 2025 02:59:32 -0700 Subject: [PATCH] edit state and apply state should work, edit UI --- .../contrib/void/browser/chatThreadService.ts | 406 +++++++++--------- .../src/markdown/ApplyBlockHoverButtons.tsx | 24 +- .../browser/react/src/markdown/BlockCode.tsx | 1 - .../react/src/markdown/ChatMarkdownRender.tsx | 5 +- .../react/src/sidebar-tsx/SidebarChat.tsx | 202 +++++---- .../void/common/helpers/getLanguage.ts | 4 +- 6 files changed, 336 insertions(+), 306 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index 5cb20f8e..caad3b73 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -23,7 +23,7 @@ import { IToolsService } from './toolsService.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; -import { ChatMessage, CodespanLocationLink, StagingSelectionItem, ToolRequestApproval } from '../common/chatThreadServiceTypes.js'; +import { ChatMessage, CodespanLocationLink, StagingSelectionItem, ToolMessage, ToolRequestApproval } from '../common/chatThreadServiceTypes.js'; import { Position } from '../../../../editor/common/core/position.js'; import { ITerminalToolService } from './terminalToolService.js'; @@ -239,224 +239,224 @@ class ChatThreadService extends Disposable implements IChatThreadService { } const threads = this._convertThreadDataFromStorage(threadsStr); - // threads['abc'] = { - // id: 'abc', - // createdAt: new Date().toISOString(), - // lastModified: new Date().toISOString(), - // messages: [ - // { - // role: 'tool', - // name: 'pathname_search', - // id: 'tool-1', - // paramsStr: '{"query": "hello", "pageNumber": 0}', - // content: '/users/andrew/void/Desktop/etc/abc.txt', - // result: { type: 'success', params: { queryStr: 'hello', pageNumber: 0 }, value: { uris: [URI.file('/Users/username/Downloads/helloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo.txt'), URI.file('/Users/username/Downloads/hello1.txt'), URI.file('/Users/username/Downloads/hello2.txt'), URI.file('/Users/username/Downloads/hello3.txt'), URI.file('/Users/username/hello.txt')], hasNextPage: true } }, - // } satisfies ToolMessage<'pathname_search'>, - // { - // role: 'tool', - // name: 'pathname_search', - // id: 'tool-1', - // paramsStr: '{"query": "hello", "pageNumber": 0}', - // content: '/users/andrew/void/Desktop/etc/abc.txt', - // result: { type: 'success', params: { queryStr: 'hello', pageNumber: 0 }, value: { uris: [], hasNextPage: false } }, - // } satisfies ToolMessage<'pathname_search'>, + threads['abc'] = { + id: 'abc', + createdAt: new Date().toISOString(), + lastModified: new Date().toISOString(), + messages: [ + { + role: 'tool', + name: 'pathname_search', + id: 'tool-1', + paramsStr: '{"query": "hello", "pageNumber": 0}', + content: '/users/andrew/void/Desktop/etc/abc.txt', + result: { type: 'success', params: { queryStr: 'hello', pageNumber: 0 }, value: { uris: [URI.file('/Users/username/Downloads/helloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo.txt'), URI.file('/Users/username/Downloads/hello1.txt'), URI.file('/Users/username/Downloads/hello2.txt'), URI.file('/Users/username/Downloads/hello3.txt'), URI.file('/Users/username/hello.txt')], hasNextPage: true } }, + } satisfies ToolMessage<'pathname_search'>, + { + role: 'tool', + name: 'pathname_search', + id: 'tool-1', + paramsStr: '{"query": "hello", "pageNumber": 0}', + content: '/users/andrew/void/Desktop/etc/abc.txt', + result: { type: 'success', params: { queryStr: 'hello', pageNumber: 0 }, value: { uris: [], hasNextPage: false } }, + } satisfies ToolMessage<'pathname_search'>, - // // { - // // role: 'tool_request', - // // name: 'pathname_search', - // // params: { queryStr: 'hello', pageNumber: 0 }, - // // paramsStr: '{"query": "hello", "pageNumber": 0}', - // // voidToolId: 'request-1', - // // } satisfies ToolRequestApproval<'pathname_search'>, + // { + // role: 'tool_request', + // name: 'pathname_search', + // params: { queryStr: 'hello', pageNumber: 0 }, + // paramsStr: '{"query": "hello", "pageNumber": 0}', + // voidToolId: 'request-1', + // } satisfies ToolRequestApproval<'pathname_search'>, - // { - // role: 'tool', - // name: 'list_dir', - // id: 'tool-2', - // paramsStr: '{"uri": "/Users/username/Documents"}', - // content: 'Directory listing of /Users/username/Documents', - // result: { - // type: 'success', - // params: { rootURI: URI.file('/Users/username/Documents'), pageNumber: 1, }, - // value: { - // children: [ - // { uri: URI.file('/Users/username/Documents/file1.txt'), name: 'file1.txt', isDirectory: false, isSymbolicLink: false }, - // { uri: URI.file('/Users/username/Documents/folder1'), name: 'folder1', isDirectory: true, isSymbolicLink: false } - // ], - // hasNextPage: true, - // hasPrevPage: true, - // itemsRemaining: 5, - // } - // }, - // } satisfies ToolMessage<'list_dir'>, + { + role: 'tool', + name: 'list_dir', + id: 'tool-2', + paramsStr: '{"uri": "/Users/username/Documents"}', + content: 'Directory listing of /Users/username/Documents', + result: { + type: 'success', + params: { rootURI: URI.file('/Users/username/Documents'), pageNumber: 1, }, + value: { + children: [ + { uri: URI.file('/Users/username/Documents/file1.txt'), name: 'file1.txt', isDirectory: false, isSymbolicLink: false }, + { uri: URI.file('/Users/username/Documents/folder1'), name: 'folder1', isDirectory: true, isSymbolicLink: false } + ], + hasNextPage: true, + hasPrevPage: true, + itemsRemaining: 5, + } + }, + } satisfies ToolMessage<'list_dir'>, - // // { - // // role: 'tool_request', - // // name: 'list_dir', - // // params: { rootURI: URI.file('/Users/username/Documents'), pageNumber: 0 }, - // // paramsStr: '{"uri": "/Users/username/Documents"}', - // // voidToolId: 'request-2', - // // } satisfies ToolRequestApproval<'list_dir'>, + // { + // role: 'tool_request', + // name: 'list_dir', + // params: { rootURI: URI.file('/Users/username/Documents'), pageNumber: 0 }, + // paramsStr: '{"uri": "/Users/username/Documents"}', + // voidToolId: 'request-2', + // } satisfies ToolRequestApproval<'list_dir'>, - // { - // role: 'tool', - // name: 'read_file', - // id: 'tool-3', - // paramsStr: '{"uri": "/Users/username/Documents/file1.txt"}', - // content: 'Content of file1.txt\nThis is a sample file.\nHello world!', - // result: { - // type: 'success', - // params: { uri: URI.file('/Users/username/Documents/file1.txt'), pageNumber: 0 }, - // value: { fileContents: 'Content of file1.txt\nThis is a sample file.\nHello world!', hasNextPage: false } - // }, - // } satisfies ToolMessage<'read_file'>, + { + role: 'tool', + name: 'read_file', + id: 'tool-3', + paramsStr: '{"uri": "/Users/username/Documents/file1.txt"}', + content: 'Content of file1.txt\nThis is a sample file.\nHello world!', + result: { + type: 'success', + params: { uri: URI.file('/src/vs/workbench/hi'), pageNumber: 0 }, + value: { fileContents: 'Content of file1.txt\nThis is a sample file.\nHello world!', hasNextPage: false } + }, + } satisfies ToolMessage<'read_file'>, - // // { - // // role: 'tool_request', - // // name: 'read_file', - // // params: { uri: URI.file('/Users/username/Documents/file1.txt'), pageNumber: 0 }, - // // paramsStr: '{"uri": "/Users/username/Documents/file1.txt"}', - // // voidToolId: 'request-3', - // // } satisfies ToolRequestApproval<'read_file'>, + // { + // role: 'tool_request', + // name: 'read_file', + // params: { uri: URI.file('/Users/username/Documents/file1.txt'), pageNumber: 0 }, + // paramsStr: '{"uri": "/Users/username/Documents/file1.txt"}', + // voidToolId: 'request-3', + // } satisfies ToolRequestApproval<'read_file'>, - // { - // role: 'tool', - // name: 'search', - // id: 'tool-4', - // paramsStr: '{"query": "function main"}', - // content: 'Found matches in 3 files', - // result: { - // type: 'success', - // params: { queryStr: 'function main', pageNumber: 0 }, - // value: { - // uris: [ - // URI.file('/Users/username/Project/main.js'), - // URI.file('/Users/username/Project/src/app.js'), - // URI.file('/Users/username/Project/test/test.js') - // ], - // hasNextPage: false - // } - // }, - // } satisfies ToolMessage<'search'>, + { + role: 'tool', + name: 'search', + id: 'tool-4', + paramsStr: '{"query": "function main"}', + content: 'Found matches in 3 files', + result: { + type: 'success', + params: { queryStr: 'function main', pageNumber: 0 }, + value: { + uris: [ + URI.file('/Users/username/Project/main.js'), + URI.file('/Users/username/Project/src/app.js'), + URI.file('/Users/username/Project/test/test.js') + ], + hasNextPage: false + } + }, + } satisfies ToolMessage<'search'>, - // // { - // // role: 'tool_request', - // // name: 'search', - // // params: { queryStr: 'function main', pageNumber: 0 }, - // // paramsStr: '{"query": "function main"}', - // // voidToolId: 'request-4', - // // } satisfies ToolRequestApproval<'search'>, + // { + // role: 'tool_request', + // name: 'search', + // params: { queryStr: 'function main', pageNumber: 0 }, + // paramsStr: '{"query": "function main"}', + // voidToolId: 'request-4', + // } satisfies ToolRequestApproval<'search'>, - // // --- + // --- - // { - // role: 'tool', - // name: 'edit', - // id: 'tool-5', - // paramsStr: '{"uri": "/Users/username/Project/main.js", "changeDescription": "Add console.log statement"}', - // content: 'Successfully edited the file at /Users/username/Project/main.js', - // result: { - // type: 'success', - // params: { uri: URI.file('/Users/username/Project/main.js'), changeDescription: 'Add console.log statement' }, - // value: {} - // }, - // } satisfies ToolMessage<'edit'>, - // { - // role: 'tool_request', - // name: 'edit', - // params: { uri: URI.file('/Users/username/Project/main.js'), changeDescription: 'Add console.log statement' }, - // paramsStr: '{"uri": "/Users/username/Project/main.js", "changeDescription": "Add console.log statement"}', - // voidToolId: 'request-5', - // } satisfies ToolRequestApproval<'edit'>, + { + role: 'tool', + name: 'edit', + id: 'tool-5', + paramsStr: '{"uri": "/Users/username/Project/main.js", "changeDescription": "Add console.log statement"}', + content: 'Successfully edited the file at /Users/username/Project/main.js', + result: { + type: 'success', + params: { uri: URI.file('/Users/username/Project/main.js'), changeDescription: 'Add console.log statement' }, + value: {} + }, + } satisfies ToolMessage<'edit'>, + { + role: 'tool_request', + name: 'edit', + params: { uri: URI.file('/Users/username/Project/main.js'), changeDescription: 'Add console.log statement' }, + paramsStr: '{"uri": "/Users/username/Project/main.js", "changeDescription": "Add console.log statement"}', + voidToolId: 'request-5', + } satisfies ToolRequestApproval<'edit'>, - // { - // role: 'tool', - // name: 'create_uri', - // id: 'tool-6', - // paramsStr: '{"uri": "/Users/username/Project/new-file.js"}', - // content: 'Successfully created file at /Users/username/Project/new-file.js', - // result: { - // type: 'success', - // params: { uri: URI.file('/Users/username/Project/new-file.js'), isFolder: false }, - // value: {} - // }, - // } satisfies ToolMessage<'create_uri'>, - // { - // role: 'tool_request', - // name: 'create_uri', - // params: { uri: URI.file('/Users/username/Project/new-file.js'), isFolder: false }, - // paramsStr: '{"uri": "/Users/username/Project/new-file.js"}', - // voidToolId: 'request-6', - // } satisfies ToolRequestApproval<'create_uri'>, + { + role: 'tool', + name: 'create_uri', + id: 'tool-6', + paramsStr: '{"uri": "/Users/username/Project/new-file.js"}', + content: 'Successfully created file at /Users/username/Project/new-file.js', + result: { + type: 'success', + params: { uri: URI.file('Users/andrew/Desktop/void/src/vs/workbench/hi'), isFolder: false }, + value: {} + }, + } satisfies ToolMessage<'create_uri'>, + { + role: 'tool_request', + name: 'create_uri', + params: { uri: URI.file('/Users/username/Project/new-file.js'), isFolder: false }, + paramsStr: '{"uri": "/Users/username/Project/new-file.js"}', + voidToolId: 'request-6', + } satisfies ToolRequestApproval<'create_uri'>, - // { - // role: 'tool', - // name: 'delete_uri', - // id: 'tool-7', - // paramsStr: '{"uri": "/Users/username/Project/old-file.js", "params": ""}', - // content: 'Successfully deleted file at /Users/username/Project/old-file.js', - // result: { - // type: 'success', - // params: { uri: URI.file('/Users/username/Project/old-file.js'), isRecursive: false, isFolder: false }, - // value: {} - // }, - // } satisfies ToolMessage<'delete_uri'>, - // { - // role: 'tool_request', - // name: 'delete_uri', - // params: { uri: URI.file('/Users/username/Project/old-file.js'), isRecursive: false, isFolder: false }, - // paramsStr: '{"uri": "/Users/username/Project/old-file.js", "params": ""}', - // voidToolId: 'request-7', - // } satisfies ToolRequestApproval<'delete_uri'>, + { + role: 'tool', + name: 'delete_uri', + id: 'tool-7', + paramsStr: '{"uri": "/Users/username/Project/old-file.js", "params": ""}', + content: 'Successfully deleted file at /Users/username/Project/old-file.js', + result: { + type: 'success', + params: { uri: URI.file('/Users/username/Project/old-file.js'), isRecursive: false, isFolder: false }, + value: {} + }, + } satisfies ToolMessage<'delete_uri'>, + { + role: 'tool_request', + name: 'delete_uri', + params: { uri: URI.file('/Users/username/Project/old-file.js'), isRecursive: false, isFolder: false }, + paramsStr: '{"uri": "/Users/username/Project/old-file.js", "params": ""}', + voidToolId: 'request-7', + } satisfies ToolRequestApproval<'delete_uri'>, - // { - // role: 'tool', - // name: 'terminal_command', - // id: 'tool-8', - // paramsStr: '{"command": "npm install", "waitForCompletion": "true"}', - // content: 'Command executed: npm install\nAdded 123 packages in 3.5s', - // result: { - // type: 'success', - // params: { command: 'npm install', proposedTerminalId: '1', waitForCompletion: true }, - // value: { - // terminalId: '1', - // didCreateTerminal: false, - // result: 'Added 123 packages in 3.5s', - // resolveReason: { type: 'done', exitCode: 0 } - // } - // }, - // } satisfies ToolMessage<'terminal_command'>, - // { - // role: 'tool_request', - // name: 'terminal_command', - // params: { command: 'npm install', proposedTerminalId: '1', waitForCompletion: true }, - // paramsStr: '{"command": "npm install", "waitForCompletion": "true"}', - // voidToolId: 'request-8', - // } satisfies ToolRequestApproval<'terminal_command'>, + { + role: 'tool', + name: 'terminal_command', + id: 'tool-8', + paramsStr: '{"command": "npm install", "waitForCompletion": "true"}', + content: 'Command executed: npm install\nAdded 123 packages in 3.5s', + result: { + type: 'success', + params: { command: 'npm install', proposedTerminalId: '1', waitForCompletion: true }, + value: { + terminalId: '1', + didCreateTerminal: false, + result: 'Added 123 packages in 3.5s', + resolveReason: { type: 'done', exitCode: 0 } + } + }, + } satisfies ToolMessage<'terminal_command'>, + { + role: 'tool_request', + name: 'terminal_command', + params: { command: 'npm install', proposedTerminalId: '1', waitForCompletion: true }, + paramsStr: '{"command": "npm install", "waitForCompletion": "true"}', + voidToolId: 'request-8', + } satisfies ToolRequestApproval<'terminal_command'>, - // // Examples of error and rejected states - // { - // role: 'tool', - // name: 'pathname_search', - // id: 'tool-error', - // paramsStr: '{"query": "invalid**query"}', - // content: 'Error: Invalid search pattern', - // result: { type: 'error', params: { queryStr: 'invalid**query', pageNumber: 0 }, value: 'Error: Invalid search pattern' }, - // } satisfies ToolMessage<'pathname_search'>, + // Examples of error and rejected states + { + role: 'tool', + name: 'pathname_search', + id: 'tool-error', + paramsStr: '{"query": "invalid**query"}', + content: 'Error: Invalid search pattern', + result: { type: 'error', params: { queryStr: 'invalid**query', pageNumber: 0 }, value: 'Error: Invalid search pattern' }, + } satisfies ToolMessage<'pathname_search'>, - // { - // role: 'tool', - // name: 'pathname_search', - // id: 'tool-rejected', - // paramsStr: '{"query": "sensitive-data"}', - // content: 'Tool call was rejected by the user.', - // result: { type: 'rejected', params: { queryStr: 'sensitive-data', pageNumber: 0 } }, - // } satisfies ToolMessage<'pathname_search'>, - // ], - // state: defaultThreadState, - // } + { + role: 'tool', + name: 'pathname_search', + id: 'tool-rejected', + paramsStr: '{"query": "sensitive-data"}', + content: 'Tool call was rejected by the user.', + result: { type: 'rejected', params: { queryStr: 'sensitive-data', pageNumber: 0 } }, + } satisfies ToolMessage<'pathname_search'>, + ], + state: defaultThreadState, + } return threads } diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx index 8d75d801..37977c40 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx @@ -98,7 +98,7 @@ const CopyButton = ({ codeStr }: { codeStr: string }) => { const applyingURIOfApplyBoxIdRef: { current: { [applyBoxId: string]: URI | undefined } } = { current: {} } -export const useApplyButtonHTML = ({ codeStr, applyBoxId }: { codeStr: string, applyBoxId: string }) => { +export const useApplyButtonHTML = ({ codeStr, applyBoxId, uri }: { codeStr: string, applyBoxId: string, uri: URI | 'current' }) => { const settingsState = useSettingsState() const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || !applyBoxId @@ -112,13 +112,16 @@ export const useApplyButtonHTML = ({ codeStr, applyBoxId }: { codeStr: string, a const getUriBeingApplied = useCallback(() => applyingURIOfApplyBoxIdRef.current[applyBoxId] ?? null, [applyBoxId]) const getStreamState = useCallback(() => editCodeService.getURIStreamState({ uri: getUriBeingApplied() }), [editCodeService, getUriBeingApplied]) - // listen for stream updates + // listen for stream updates on this box useURIStreamState( - useCallback((uri, newStreamState) => { - const shouldUpdate = getUriBeingApplied()?.fsPath === uri.fsPath + useCallback((uri_, newStreamState) => { + const shouldUpdate = ( + getUriBeingApplied()?.fsPath === uri_.fsPath + || (uri === 'current' ? false : uri.fsPath === uri_.fsPath) + ) if (!shouldUpdate) return rerender(c => c + 1) - }, [applyBoxId, editCodeService, getUriBeingApplied]) + }, [applyBoxId, editCodeService, getUriBeingApplied, uri]) ) const onClickSubmit = useCallback(async () => { @@ -127,14 +130,14 @@ export const useApplyButtonHTML = ({ codeStr, applyBoxId }: { codeStr: string, a const [newApplyingUri, _] = await editCodeService.startApplying({ from: 'ClickApply', applyStr: codeStr, - uri: 'current', + uri: uri, startBehavior: 'reject-conflicts', }) ?? [] applyingURIOfApplyBoxIdRef.current[applyBoxId] = newApplyingUri ?? undefined rerender(c => c + 1) metricsService.capture('Apply Code', { length: codeStr.length }) // capture the length only - }, [isDisabled, getStreamState, editCodeService, codeStr, applyBoxId, metricsService]) + }, [isDisabled, getStreamState, editCodeService, codeStr, uri, applyBoxId, metricsService]) const onInterrupt = useCallback(() => { @@ -263,17 +266,18 @@ export const BlockCodeApplyWrapper = ({ applyBoxId, language, canApply, - + uri, }: { initValue: string; children: React.ReactNode; applyBoxId: string; canApply: boolean; language: string; + uri: URI | 'current', }) => { - const { statusIndicatorHTML, buttonsHTML } = useApplyButtonHTML({ codeStr: initValue, applyBoxId }) + const { statusIndicatorHTML, buttonsHTML } = useApplyButtonHTML({ codeStr: initValue, applyBoxId, uri }) return
-
+
{buttonsHTML}
diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx index 5a403ad5..114143f0 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------*/ import { VoidCodeEditor, VoidCodeEditorProps } from '../util/inputs.js'; -import { BlockCodeApplyWrapper, useApplyButtonHTML } from './ApplyBlockHoverButtons.js'; export const BlockCode = ({ ...codeEditorProps }: VoidCodeEditorProps) => { diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index 9823f62a..f21bef23 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -20,7 +20,7 @@ export type ChatMessageLocation = { type ApplyBoxLocation = ChatMessageLocation & { tokenIdx: string } -const getApplyBoxId = ({ threadId, messageIdx, tokenIdx }: ApplyBoxLocation) => { +export const getApplyBoxId = ({ threadId, messageIdx, tokenIdx }: ApplyBoxLocation) => { return `${threadId}-${messageIdx}-${tokenIdx}` } @@ -120,7 +120,7 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, .. const firstLineIsURI = URI.isUri(firstLine) const contents = firstLineIsURI ? (remainingContents || '') : t.text // exclude first-line URI from contents - // figure out langauge + // figure out langauge and URI let language: string | undefined = undefined let uri: URI | undefined = undefined if (t.lang) { // a language was provided. empty string is common so check truthy, not just undefined @@ -151,6 +151,7 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, .. applyBoxId={applyBoxId} initValue={contents} language={language} + uri={uri || 'current'} > { // 'unixify' path pathStr = pathStr.replace(/[/\\]+/g, '/') // replace any / or \ or \\ with / const parts = pathStr.split('/') // split on / + if (parts.length === 0) return pathStr return parts[parts.length - 1] } @@ -679,7 +681,7 @@ type ToolHeaderParams = { onClick?: () => void; } -const ToolHeaderComponent = ({ +const ToolHeaderWrapper = ({ icon, title, desc1, @@ -696,7 +698,7 @@ const ToolHeaderComponent = ({ const isClickable = !!(isDropdown || onClick) return (
-
+
{/* header */}
)} -
+
+ {/* left */}
{title} - - {/* Fixed description with proper ellipsis */} {desc1}
+ + {/* right */}
{desc2 && {desc2} @@ -733,13 +736,13 @@ const ToolHeaderComponent = ({ {/* children */} {
{children}
}
-
- ); +
); }; @@ -939,28 +942,6 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isLoading }: ChatBubble -export const ToolContentsWrapper = ({ children, className }: { children: React.ReactNode, className?: string }) => { - return
-
- {children} -
-
-} -const ListableToolItem = ({ name, onClick, isSmall, className }: { name: string, onClick?: () => void, isSmall?: boolean, className?: string }) => { - return
-
-
{name}
-
-} - - const AssistantMessageComponent = ({ chatMessage, isLoading, messageIdx, isLast }: ChatBubbleProps & { chatMessage: ChatMessage & { role: 'assistant' } }) => { const accessor = useAccessor() @@ -1141,9 +1122,54 @@ const ToolRequestAcceptRejectButtons = ({ voidToolId }: { voidToolId: string })
} +export const ToolContentsWrapper = ({ children, className }: { children: React.ReactNode, className?: string }) => { + return
+
+ {children} +
+
+} +const ListableToolItem = ({ name, onClick, isSmall, className, showDot }: { name: React.ReactNode, onClick?: () => void, isSmall?: boolean, className?: string, showDot?: boolean }) => { + return
+ {showDot === false ? null :
} +
{name}
+
+} + +const EditToolChildren = ({ uri, changeDescription }: { uri: URI, changeDescription: string }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') + return + { commandService.executeCommand('vscode.open', uri, { preview: true }) }} + /> +
+ +
+
+} +const EditToolApplyButton = ({ changeDescription, applyBoxId, uri }: { changeDescription: string, applyBoxId: string, uri: URI }) => { + const { statusIndicatorHTML, buttonsHTML } = useApplyButtonHTML({ codeStr: changeDescription, applyBoxId, uri }) + return
+ {statusIndicatorHTML} + {buttonsHTML} +
+} + + const toolNameToComponent: { [T in ToolName]: { requestWrapper: T extends ToolNameWithApproval ? ((props: { toolRequest: ToolRequestApproval }) => React.ReactNode) : null, - resultWrapper: (props: { toolMessage: ToolMessage }) => React.ReactNode, + resultWrapper: (props: { toolMessage: ToolMessage, messageIdx: number }) => React.ReactNode, } } = { 'read_file': { requestWrapper: null, @@ -1171,7 +1197,7 @@ const toolNameToComponent: { [T in ToolName]: { } - return + return }, }, 'list_dir': { @@ -1192,10 +1218,8 @@ const toolNameToComponent: { [T in ToolName]: { if (toolMessage.result.type === 'success') { const { value, params } = toolMessage.result componentParams.numResults = value.children?.length - componentParams.children = - {!value.children || (value.children.length ?? 0) === 0 ? <> - - : <> + componentParams.children = !value.children || (value.children.length ?? 0) === 0 ? undefined + : {value.children.map((child, i) => ( { @@ -1206,8 +1230,7 @@ const toolNameToComponent: { [T in ToolName]: { {value.hasNextPage && } - } - + } else { componentParams.children = <> @@ -1215,7 +1238,7 @@ const toolNameToComponent: { [T in ToolName]: { } - return + return } }, 'pathname_search': { @@ -1235,10 +1258,8 @@ const toolNameToComponent: { [T in ToolName]: { if (toolMessage.result.type === 'success') { const { value, params } = toolMessage.result componentParams.numResults = value.uris.length - componentParams.children = - {value.uris.length === 0 ? <> - - : <> + componentParams.children = value.uris.length === 0 ? undefined + : {value.uris.map((uri, i) => ( { commandService.executeCommand('vscode.open', uri, { preview: true }) }} @@ -1246,8 +1267,8 @@ const toolNameToComponent: { [T in ToolName]: { {value.hasNextPage && } - } - + + } else { componentParams.children = <> @@ -1255,7 +1276,7 @@ const toolNameToComponent: { [T in ToolName]: { } - return + return } }, 'search': { @@ -1275,10 +1296,8 @@ const toolNameToComponent: { [T in ToolName]: { if (toolMessage.result.type === 'success') { const { value, params } = toolMessage.result componentParams.numResults = value.uris.length - componentParams.children = - {value.uris.length === 0 ? <> - - : <> + componentParams.children = value.uris.length === 0 ? undefined + : {value.uris.map((uri, i) => ( { commandService.executeCommand('vscode.open', uri, { preview: true }) }} @@ -1286,15 +1305,15 @@ const toolNameToComponent: { [T in ToolName]: { {value.hasNextPage && } - } - + + } else { componentParams.children = <> {toolMessage.result.value} } - return + return } }, @@ -1304,6 +1323,7 @@ const toolNameToComponent: { [T in ToolName]: { requestWrapper: ({ toolRequest }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') + const explorerService = accessor.get('IExplorerService') const title = toolNameToTitle[toolRequest.name].proposed const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params) const icon = null @@ -1312,9 +1332,13 @@ const toolNameToComponent: { [T in ToolName]: { const componentParams: ToolHeaderParams = { title, desc1, isError, icon, } const { params } = toolRequest - componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) } - return + // TODO!!! would be cool to open up the lowest parent that exists + // componentParams.onClick = () => { + // // open the parent + // } + + return }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() @@ -1341,7 +1365,7 @@ const toolNameToComponent: { [T in ToolName]: { } - return + return } }, 'delete_uri': { @@ -1358,7 +1382,7 @@ const toolNameToComponent: { [T in ToolName]: { const { params } = toolRequest componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) } - return + return }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() @@ -1384,7 +1408,7 @@ const toolNameToComponent: { [T in ToolName]: { } - return + return } }, 'edit': { @@ -1399,21 +1423,16 @@ const toolNameToComponent: { [T in ToolName]: { const componentParams: ToolHeaderParams = { title, desc1, isError, icon, } const { params } = toolRequest - componentParams.children = - { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} - /> -
- -
-
+ componentParams.children = - return + return }, - resultWrapper: ({ toolMessage }) => { + resultWrapper: ({ toolMessage, messageIdx }) => { const accessor = useAccessor() - const commandService = accessor.get('ICommandService') + const chatThreadsService = accessor.get('IChatThreadService') const title = toolNameToTitle[toolMessage.name].past const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params) const icon = null @@ -1421,15 +1440,25 @@ const toolNameToComponent: { [T in ToolName]: { const isError = toolMessage.result.type === 'error' const componentParams: ToolHeaderParams = { title, desc1, isError, icon } - if (toolMessage.result.type === 'success') { + if (toolMessage.result.type === 'success' || toolMessage.result.type === 'rejected') { const { params } = toolMessage.result - componentParams.children = - componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) } - } - else if (toolMessage.result.type === 'rejected') { - const { params } = toolMessage.result - componentParams.children = - componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) } + + const threadId = chatThreadsService.getCurrentThread().id + const applyBoxId = getApplyBoxId({ + threadId: threadId, + messageIdx: messageIdx, + tokenIdx: 'N/A', + }) + + componentParams.children = + componentParams.desc2 = } else if (toolMessage.result.type === 'error') { componentParams.children = <> @@ -1437,7 +1466,7 @@ const toolNameToComponent: { [T in ToolName]: { } - return + return } }, 'terminal_command': { @@ -1458,8 +1487,7 @@ const toolNameToComponent: { [T in ToolName]: { if (!waitForCompletion) componentParams.desc2 = '(background task)' - // TODO!!! open terminal - return + return }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() @@ -1510,9 +1538,7 @@ const toolNameToComponent: { [T in ToolName]: { } - // TODO!!! open terminal - - return + return } } }; @@ -1551,8 +1577,8 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx, isLast }: ChatBubblePr } else if (role === 'tool') { - const ToolResultWrapper = toolNameToComponent[chatMessage.name].resultWrapper as React.FC<{ toolMessage: any }> // ts isnt smart enough... - return + const ToolResultWrapper = toolNameToComponent[chatMessage.name].resultWrapper as React.FC<{ toolMessage: any, messageIdx: number }> // ts isnt smart enough... + return } } diff --git a/src/vs/workbench/contrib/void/common/helpers/getLanguage.ts b/src/vs/workbench/contrib/void/common/helpers/getLanguage.ts index da2c82d1..47835df5 100644 --- a/src/vs/workbench/contrib/void/common/helpers/getLanguage.ts +++ b/src/vs/workbench/contrib/void/common/helpers/getLanguage.ts @@ -67,10 +67,10 @@ const markdownLangToVscodeLang: { [key: string]: string } = { 'less': 'less', 'javascript': 'typescript', 'js': 'typescript', // use more general renderer - 'jsx': 'typescript', + 'jsx': 'typescriptreact', 'typescript': 'typescript', 'ts': 'typescript', - 'tsx': 'typescript', + 'tsx': 'typescriptreact', 'json': 'json', 'jsonc': 'json',