mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
style updates progress
This commit is contained in:
parent
dbdd40bd3d
commit
8a6b75b6bb
8 changed files with 458 additions and 208 deletions
|
|
@ -237,7 +237,228 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
if (!threadsStr) {
|
||||
return null
|
||||
}
|
||||
return this._convertThreadDataFromStorage(threadsStr);
|
||||
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'>,
|
||||
|
||||
// // {
|
||||
// // 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_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_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_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: '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: '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'>,
|
||||
|
||||
|
||||
|
||||
// // 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,
|
||||
// }
|
||||
|
||||
return threads
|
||||
}
|
||||
|
||||
private _storeAllThreads(threads: ChatThreads) {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
.void-scrollable-element::-webkit-scrollbar,
|
||||
.void-scrollable-element *::-webkit-scrollbar {
|
||||
width: 14px !important;
|
||||
height: 14px !important;
|
||||
height: 4px !important;
|
||||
}
|
||||
|
||||
.void-scrollable-element::-webkit-scrollbar-track,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import { isFeatureNameDisabled } from '../../../../common/voidSettingsTypes.js'
|
|||
import { URI } from '../../../../../../../base/common/uri.js'
|
||||
import { LucideIcon, RotateCw } from 'lucide-react'
|
||||
import { Check, X, Square, Copy, Play, } from 'lucide-react'
|
||||
import { ToolContentsWrapper } from '../sidebar-tsx/SidebarChat.js'
|
||||
import { ChatMarkdownRender } from './ChatMarkdownRender.js'
|
||||
|
||||
enum CopyButtonText {
|
||||
Idle = 'Copy',
|
||||
|
|
@ -27,7 +29,8 @@ export const IconShell1 = ({ onClick, title, Icon, disabled, className }: IconBu
|
|||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
className={`
|
||||
size-[24px]
|
||||
size-[22px]
|
||||
p-[4px]
|
||||
flex items-center justify-center
|
||||
text-sm bg-void-bg-3 text-void-fg-1
|
||||
hover:brightness-110
|
||||
|
|
@ -36,28 +39,28 @@ export const IconShell1 = ({ onClick, title, Icon, disabled, className }: IconBu
|
|||
${className}
|
||||
`}
|
||||
>
|
||||
<Icon size={16} />
|
||||
<Icon />
|
||||
</button>
|
||||
)
|
||||
|
||||
|
||||
export const IconShell2 = ({ onClick, title, Icon, disabled, className }: IconButtonProps) => (
|
||||
<button
|
||||
title={title}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
className={`
|
||||
size-[24px]
|
||||
flex items-center justify-center
|
||||
text-sm
|
||||
hover:opacity-80
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
<Icon size={16} />
|
||||
</button>
|
||||
)
|
||||
// export const IconShell2 = ({ onClick, title, Icon, disabled, className }: IconButtonProps) => (
|
||||
// <button
|
||||
// title={title}
|
||||
// disabled={disabled}
|
||||
// onClick={onClick}
|
||||
// className={`
|
||||
// size-[24px]
|
||||
// flex items-center justify-center
|
||||
// text-sm
|
||||
// hover:opacity-80
|
||||
// disabled:opacity-50 disabled:cursor-not-allowed
|
||||
// ${className}
|
||||
// `}
|
||||
// >
|
||||
// <Icon size={16} />
|
||||
// </button>
|
||||
// )
|
||||
|
||||
const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!'
|
||||
|
||||
|
|
@ -230,17 +233,16 @@ export const useApplyButtonHTML = ({ codeStr, applyBoxId }: { codeStr: string, a
|
|||
</>
|
||||
}
|
||||
|
||||
const statusIndicatorHTML = <div className='flex flex-row gap-2 items-center'>
|
||||
const statusIndicatorHTML = <div className='flex flex-row items-center size-4'>
|
||||
<div
|
||||
className={`size-1.5 rounded-full border
|
||||
className={` size-1.5 rounded-full border
|
||||
${currStreamState === 'idle' ? 'bg-void-bg-3 border-void-border-1' :
|
||||
currStreamState === 'streaming' ? 'bg-orange-500 border-orange-500 shadow-[0_0_4px_0px_rgba(234,88,12,0.6)]' :
|
||||
currStreamState === 'acceptRejectAll' ? 'bg-green-500 border-green-500 shadow-[0_0_4px_0px_rgba(22,163,74,0.6)]' :
|
||||
'bg-void-border-1 border-void-border-1'
|
||||
}`
|
||||
}
|
||||
>
|
||||
</div>
|
||||
/>
|
||||
</div>
|
||||
|
||||
return {
|
||||
|
|
@ -248,5 +250,52 @@ export const useApplyButtonHTML = ({ codeStr, applyBoxId }: { codeStr: string, a
|
|||
buttonsHTML
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const BlockCodeApplyWrapper = ({
|
||||
children,
|
||||
initValue,
|
||||
applyBoxId,
|
||||
language,
|
||||
canApply,
|
||||
|
||||
}: {
|
||||
initValue: string;
|
||||
children: React.ReactNode;
|
||||
applyBoxId: string;
|
||||
canApply: boolean;
|
||||
language: string;
|
||||
}) => {
|
||||
|
||||
|
||||
const { statusIndicatorHTML, buttonsHTML } = useApplyButtonHTML({ codeStr: initValue, applyBoxId })
|
||||
|
||||
return <div
|
||||
className='border border-void-border-3 rounded overflow-hidden bg-void-bg-3'
|
||||
>
|
||||
|
||||
{/* header */}
|
||||
<div className=" select-none flex justify-between items-center py-1 px-2 border-b border-void-border-3 cursor-default">
|
||||
<div className="flex items-center">
|
||||
{statusIndicatorHTML}
|
||||
<span className="text-[13px] font-light text-void-fg-3">
|
||||
{language || 'text'}
|
||||
</span>
|
||||
</div>
|
||||
<div className={`${canApply ? '' : 'hidden'} flex gap-1`}>
|
||||
{buttonsHTML}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* contents */}
|
||||
<ToolContentsWrapper>
|
||||
{children}
|
||||
</ToolContentsWrapper>
|
||||
</div>
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,54 +4,10 @@
|
|||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VoidCodeEditor, VoidCodeEditorProps } from '../util/inputs.js';
|
||||
import { useApplyButtonHTML } from './ApplyBlockHoverButtons.js';
|
||||
|
||||
export const BlockCodeWithApply = ({ initValue, language, applyBoxId }: { initValue: string, language?: string, applyBoxId: string }) => {
|
||||
|
||||
const { statusIndicatorHTML, buttonsHTML } = useApplyButtonHTML({ codeStr: initValue, applyBoxId })
|
||||
|
||||
return (
|
||||
<div className="border border-void-border-3 rounded-sm overflow-hidden bg-void-bg-2">
|
||||
<div className="flex justify-between items-center px-2 py-1 border-b border-void-border-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="text-sm opacity-50">{language || 'text'}</div>
|
||||
{statusIndicatorHTML}
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
{buttonsHTML}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BlockCode
|
||||
initValue={initValue}
|
||||
language={language}
|
||||
/>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
import { BlockCodeApplyWrapper, useApplyButtonHTML } from './ApplyBlockHoverButtons.js';
|
||||
|
||||
|
||||
export const BlockCode = ({ ...codeEditorProps }: VoidCodeEditorProps) => {
|
||||
|
||||
const isSingleLine = !codeEditorProps.initValue.includes('\n')
|
||||
|
||||
return (
|
||||
<>
|
||||
<VoidCodeEditor {...codeEditorProps} />
|
||||
|
||||
{/* <div className="relative group w-full overflow-hidden">
|
||||
{buttonsOnHover === null ? null : (
|
||||
<div className={`z-[1] absolute top-0 right-0 opacity-0 group-hover:opacity-100 duration-200 ${isSingleLine ? 'h-full flex items-center' : ''}`}>
|
||||
<div className={`flex space-x-1 ${isSingleLine ? 'pr-2' : 'p-2'}`}>
|
||||
{buttonsOnHover}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<VoidCodeEditor {...codeEditorProps} />
|
||||
</div> */}
|
||||
|
||||
</>
|
||||
)
|
||||
return <VoidCodeEditor {...codeEditorProps} />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,10 @@
|
|||
|
||||
import React, { JSX, useState } from 'react'
|
||||
import { marked, MarkedToken, Token } from 'marked'
|
||||
import { BlockCode, BlockCodeWithApply } from './BlockCode.js'
|
||||
import { BlockCode } from './BlockCode.js'
|
||||
import { convertToVscodeLang, getFirstLine, getLanguage } from '../../../../common/helpers/getLanguage.js'
|
||||
import { useApplyButtonHTML } from './ApplyBlockHoverButtons.js'
|
||||
import { useAccessor, useChatThreadsState } from '../util/services.js'
|
||||
import { Range } from '../../../../../../services/search/common/searchExtTypes.js'
|
||||
import { IRange } from '../../../../../../../base/common/range.js'
|
||||
import { BlockCodeApplyWrapper, useApplyButtonHTML } from './ApplyBlockHoverButtons.js'
|
||||
import { useAccessor } from '../util/services.js'
|
||||
import { ScrollType } from '../../../../../../../editor/common/editorCommon.js'
|
||||
import { URI } from '../../../../../../../base/common/uri.js'
|
||||
|
||||
|
|
@ -102,7 +100,7 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string
|
|||
|
||||
|
||||
export type RenderTokenOptions = { isApplyEnabled?: boolean, isLinkDetectionEnabled?: boolean }
|
||||
const RenderToken = ({ token, inPTag, chatMessageLocation, tokenIdx, ...options }: { token: Token | string, inPTag?: boolean, chatMessageLocation?: ChatMessageLocation, tokenIdx: string, } & RenderTokenOptions): JSX.Element => {
|
||||
const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, ...options }: { token: Token | string, inPTag?: boolean, codeURI?: URI, chatMessageLocation?: ChatMessageLocation, tokenIdx: string, } & RenderTokenOptions): JSX.Element => {
|
||||
const accessor = useAccessor()
|
||||
const languageService = accessor.get('ILanguageService')
|
||||
|
||||
|
|
@ -120,36 +118,45 @@ const RenderToken = ({ token, inPTag, chatMessageLocation, tokenIdx, ...options
|
|||
if (t.type === "code") {
|
||||
const [firstLine, remainingContents] = getFirstLine(t.text)
|
||||
const firstLineIsURI = URI.isUri(firstLine)
|
||||
|
||||
let language: string | undefined = undefined
|
||||
if (t.lang !== undefined) {
|
||||
// convert markdown language to language that vscode recognizes (eg markdown doesn't know bash but it does know shell)
|
||||
language = convertToVscodeLang(languageService, t.lang)
|
||||
}
|
||||
|
||||
else if (!language) { // if still no lang
|
||||
if (firstLineIsURI) { // get lang from the uri
|
||||
const uri = URI.file(firstLine)
|
||||
language = getLanguage(languageService, { uri, fileContents: remainingContents ?? undefined })
|
||||
}
|
||||
else { // get lang from the contents
|
||||
language = getLanguage(languageService, { uri: null, fileContents: remainingContents ?? undefined })
|
||||
}
|
||||
}
|
||||
const contents = firstLineIsURI ? (remainingContents || '') : t.text // exclude first-line URI from contents
|
||||
|
||||
// TODO!!! user should only be able to apply this when the code has been closed (t.raw ends with "```")
|
||||
// figure out langauge
|
||||
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
|
||||
uri = codeURI
|
||||
language = convertToVscodeLang(languageService, t.lang) // convert markdown language to language that vscode recognizes (eg markdown doesn't know bash but it does know shell)
|
||||
}
|
||||
else { // no language provided - fallback
|
||||
if (firstLineIsURI) { // get lang from the uri in the markdown
|
||||
uri = codeURI ?? URI.file(firstLine)
|
||||
language = getLanguage(languageService, { uri, fileContents: remainingContents ?? undefined })
|
||||
}
|
||||
else { // get lang from the given URI and contents
|
||||
uri = codeURI
|
||||
language = getLanguage(languageService, { uri: codeURI ?? null, fileContents: remainingContents ?? undefined })
|
||||
}
|
||||
}
|
||||
|
||||
if (options.isApplyEnabled && chatMessageLocation) {
|
||||
const isCodeblockClosed = t.raw.trimEnd().endsWith('```') // user should only be able to Apply when the code has been closed (t.raw ends with "```")
|
||||
|
||||
const applyBoxId = getApplyBoxId({
|
||||
threadId: chatMessageLocation.threadId,
|
||||
messageIdx: chatMessageLocation.messageIdx,
|
||||
tokenIdx: tokenIdx,
|
||||
})
|
||||
return <BlockCodeWithApply
|
||||
return <BlockCodeApplyWrapper
|
||||
canApply={isCodeblockClosed}
|
||||
applyBoxId={applyBoxId}
|
||||
initValue={contents}
|
||||
language={language}
|
||||
applyBoxId={applyBoxId}
|
||||
/>
|
||||
>
|
||||
<BlockCode
|
||||
initValue={contents}
|
||||
language={language}
|
||||
/>
|
||||
</BlockCodeApplyWrapper>
|
||||
}
|
||||
return <BlockCode
|
||||
initValue={contents}
|
||||
|
|
@ -239,7 +246,7 @@ const RenderToken = ({ token, inPTag, chatMessageLocation, tokenIdx, ...options
|
|||
return <li>
|
||||
<input type="checkbox" checked={t.checked} readOnly />
|
||||
<span>
|
||||
<ChatMarkdownRender chatMessageLocation={chatMessageLocation} string={t.text} inPTag={true} {...options} />
|
||||
<ChatMarkdownRender chatMessageLocation={chatMessageLocation} string={t.text} inPTag={true} codeURI={codeURI} {...options} />
|
||||
</span>
|
||||
</li>
|
||||
}
|
||||
|
|
@ -361,7 +368,7 @@ const RenderToken = ({ token, inPTag, chatMessageLocation, tokenIdx, ...options
|
|||
}
|
||||
|
||||
|
||||
export const ChatMarkdownRender = ({ string, inPTag = false, chatMessageLocation, ...options }: { string: string, inPTag?: boolean, chatMessageLocation: ChatMessageLocation | undefined } & RenderTokenOptions) => {
|
||||
export const ChatMarkdownRender = ({ string, inPTag = false, chatMessageLocation, ...options }: { string: string, inPTag?: boolean, codeURI?: URI, chatMessageLocation: ChatMessageLocation | undefined } & RenderTokenOptions) => {
|
||||
const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -696,8 +696,7 @@ const ToolHeaderComponent = ({
|
|||
const isClickable = !!(isDropdown || onClick)
|
||||
|
||||
return (<div className=''>
|
||||
<div className="border border-void-border-3 rounded px-2 py-1 bg-void-bg-2-alt overflow-hidden">
|
||||
|
||||
<div className="w-full border border-void-border-3 rounded px-2 py-1 bg-void-bg-2-alt overflow-hidden">
|
||||
{/* header */}
|
||||
<div
|
||||
className={`select-none flex items-center min-h-[24px] ${isClickable ? 'cursor-pointer hover:brightness-125 transition-all duration-150' : ''} ${!isDropdown ? 'mx-1' : ''}`}
|
||||
|
|
@ -711,12 +710,14 @@ const ToolHeaderComponent = ({
|
|||
className={`text-void-fg-3 mr-0.5 h-4 w-4 flex-shrink-0 transition-transform duration-100 ease-[cubic-bezier(0.4,0,0.2,1)] ${isExpanded ? 'rotate-90' : ''}`}
|
||||
/>
|
||||
)}
|
||||
<div className="flex items-center justify-between w-full flex-nowrap whitespace-nowrap gap-x-2">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<span className="text-void-fg-3">{title}</span>
|
||||
<span className="text-void-fg-4 text-xs italic">{desc1}</span>
|
||||
<div className="flex items-center w-full gap-x-2 overflow-hidden">
|
||||
<div className="flex items-center gap-x-2 min-w-0 overflow-hidden">
|
||||
<span className="text-void-fg-3 flex-shrink-0">{title}</span>
|
||||
|
||||
{/* Fixed description with proper ellipsis */}
|
||||
<span className="text-void-fg-4 text-xs italic truncate">{desc1}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="flex items-center gap-x-2 flex-shrink-0">
|
||||
{desc2 && <span className="text-void-fg-4 text-xs">
|
||||
{desc2}
|
||||
</span>}
|
||||
|
|
@ -731,11 +732,10 @@ const ToolHeaderComponent = ({
|
|||
</div>
|
||||
{/* children */}
|
||||
{<div
|
||||
// the py-1 here makes sure all elements in the container have py-2 total. this makes a nice animation effect during transition.
|
||||
className={`overflow-hidden transition-all duration-200 ease-in-out ${isExpanded ? 'opacity-100' : 'max-h-0 opacity-0'}
|
||||
text-void-fg-4 bg-black bg-opacity-20 border border-void-border-4 border-opacity-50 rounded-sm`}
|
||||
text-void-fg-4 bg-black bg-opacity-20 border border-void-border-4 border-opacity-50 rounded-sm`}
|
||||
>
|
||||
{children || '(no results)'}
|
||||
{children}
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -938,6 +938,29 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isLoading }: ChatBubble
|
|||
}
|
||||
|
||||
|
||||
|
||||
export const ToolContentsWrapper = ({ children, className }: { children: React.ReactNode, className?: string }) => {
|
||||
return <div className={`${className ? className : ''} max-h-64 overflow-x-auto cursor-default select-none`}>
|
||||
<div className='px-2 py-1 min-w-full'>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
const ListableToolItem = ({ name, onClick, isSmall, className }: { name: string, onClick?: () => void, isSmall?: boolean, className?: string }) => {
|
||||
return <div
|
||||
className={`
|
||||
${onClick ? 'hover:brightness-125 hover:cursor-pointer transition-all duration-200 ' : ''}
|
||||
flex items-center flex-nowrap whitespace-nowrap
|
||||
${className ? className : ''}
|
||||
`}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="flex-shrink-0"><svg className="w-1 h-1 opacity-60 mr-1.5 fill-current" viewBox="0 0 100 40"><rect x="0" y="15" width="100" height="10" /></svg></div>
|
||||
<div className={`${isSmall ? 'italic text-sm leading-4 flex items-center' : ''}`}>{name}</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
const AssistantMessageComponent = ({ chatMessage, isLoading, messageIdx, isLast }: ChatBubbleProps & { chatMessage: ChatMessage & { role: 'assistant' } }) => {
|
||||
|
||||
const accessor = useAccessor()
|
||||
|
|
@ -963,7 +986,6 @@ const AssistantMessageComponent = ({ chatMessage, isLoading, messageIdx, isLast
|
|||
className='
|
||||
text-void-fg-2
|
||||
|
||||
|
||||
prose
|
||||
prose-sm
|
||||
break-words
|
||||
|
|
@ -1014,15 +1036,15 @@ const AssistantMessageComponent = ({ chatMessage, isLoading, messageIdx, isLast
|
|||
|
||||
|
||||
// should either be past or "-ing" tense, not present tense. Eg. when the LLM searches for something, the user expects it to say "I searched for X" or "I am searching for X". Not "I search X".
|
||||
const toolNameToTitle: Record<ToolName, string> = {
|
||||
'read_file': 'Read file', // past tense
|
||||
'list_dir': 'Inspected folder', // past tense
|
||||
'pathname_search': 'Searched by file name', // past tense
|
||||
'search': 'Searched', // past tense
|
||||
'create_uri': 'Created file', // past tense
|
||||
'delete_uri': 'Deleted file', // past tense
|
||||
'edit': 'Edited file', // past tense
|
||||
'terminal_command': 'Ran terminal command' // past tense
|
||||
const toolNameToTitle: Record<ToolName, { past: string, current: string, proposed: string }> = {
|
||||
'read_file': { past: 'Read file', current: 'Reading file', proposed: 'Read file' },
|
||||
'list_dir': { past: 'Inspected folder', current: 'Inspecting folder', proposed: 'Inspect folder' },
|
||||
'pathname_search': { past: 'Searched by file name', current: 'Searching by file name', proposed: 'Search by file name' },
|
||||
'search': { past: 'Searched', current: 'Searching', proposed: 'Search' },
|
||||
'create_uri': { past: 'Created file', current: 'Creating file', proposed: 'Create file' },
|
||||
'delete_uri': { past: 'Deleted file', current: 'Deleting file', proposed: 'Delete file' },
|
||||
'edit': { past: 'Edited file', current: 'Editing file', proposed: 'Edit file' },
|
||||
'terminal_command': { past: 'Ran terminal command', current: 'Running terminal command', proposed: 'Run terminal command' }
|
||||
}
|
||||
const toolNameToDesc = (toolName: ToolName, _toolParams: ToolCallParams[ToolName] | undefined): string => {
|
||||
|
||||
|
|
@ -1128,17 +1150,17 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
resultWrapper: ({ toolMessage }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const title = toolNameToTitle[toolMessage.name]
|
||||
const title = toolNameToTitle[toolMessage.name].past
|
||||
const { uri } = toolMessage.result.params ?? {}
|
||||
const desc1 = uri ? getBasename(uri.fsPath) : '';
|
||||
const icon = null
|
||||
|
||||
if (toolMessage.result.type === 'rejected') return null
|
||||
if (toolMessage.result.type === 'rejected') return null // will never happen, not rejectable
|
||||
|
||||
const isError = toolMessage.result.type === 'error'
|
||||
const componentParams: ToolHeaderParams = { title, desc1, isError, icon }
|
||||
|
||||
if (toolMessage.result.type !== 'error') {
|
||||
if (toolMessage.result.type === 'success') {
|
||||
const { value, params } = toolMessage.result
|
||||
componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }
|
||||
if (toolMessage.result.value.hasNextPage) componentParams.desc2 = `(AI can scroll for more)`
|
||||
|
|
@ -1158,38 +1180,34 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const explorerService = accessor.get('IExplorerService')
|
||||
const title = toolNameToTitle[toolMessage.name]
|
||||
const title = toolNameToTitle[toolMessage.name].past
|
||||
const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params)
|
||||
const icon = null
|
||||
|
||||
if (toolMessage.result.type === 'rejected') return null
|
||||
if (toolMessage.result.type === 'rejected') return null // will never happen, not rejectable
|
||||
|
||||
const isError = toolMessage.result.type === 'error'
|
||||
const componentParams: ToolHeaderParams = { title, desc1, isError, icon }
|
||||
|
||||
if (toolMessage.result.type !== 'error') {
|
||||
if (toolMessage.result.type === 'success') {
|
||||
const { value, params } = toolMessage.result
|
||||
componentParams.numResults = value.children?.length
|
||||
componentParams.children = (value.children?.length ?? 0) === 0 ? null : <>
|
||||
{value.children?.map((child, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="hover:brightness-125 hover:cursor-pointer transition-all duration-200 flex items-center flex-nowrap"
|
||||
componentParams.children = <ToolContentsWrapper>
|
||||
{!value.children || (value.children.length ?? 0) === 0 ? <>
|
||||
<ListableToolItem name={'No results found.'} isSmall={true} />
|
||||
</> : <>
|
||||
{value.children.map((child, i) => (<ListableToolItem key={i}
|
||||
name={`${child.name}${child.isDirectory ? '/' : ''}`}
|
||||
onClick={() => {
|
||||
commandService.executeCommand('workbench.view.explorer');
|
||||
explorerService.select(child.uri, true);
|
||||
}}
|
||||
>
|
||||
<div className="flex-shrink-0"><svg className="w-1 h-1 opacity-60 mr-1.5 fill-current" viewBox="0 0 100 40"><rect x="0" y="15" width="100" height="10" /></svg></div>
|
||||
{`${child.name}${child.isDirectory ? '/' : ''}`}
|
||||
</div>
|
||||
))}
|
||||
{value.hasNextPage && (
|
||||
<div className="italic">
|
||||
{value.itemsRemaining} more items...
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
/>))}
|
||||
{value.hasNextPage &&
|
||||
<ListableToolItem name={`Results truncated (${value.itemsRemaining} remaining).`} isSmall={true} />
|
||||
}
|
||||
</>}
|
||||
</ToolContentsWrapper>
|
||||
}
|
||||
else {
|
||||
componentParams.children = <>
|
||||
|
|
@ -1205,37 +1223,31 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
resultWrapper: ({ toolMessage }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const title = toolNameToTitle[toolMessage.name]
|
||||
const title = toolNameToTitle[toolMessage.name].past
|
||||
const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params)
|
||||
const icon = null
|
||||
|
||||
if (toolMessage.result.type === 'rejected') return null
|
||||
if (toolMessage.result.type === 'rejected') return null // will never happen, not rejectable
|
||||
|
||||
const isError = toolMessage.result.type === 'error'
|
||||
const componentParams: ToolHeaderParams = { title, desc1, isError, icon }
|
||||
|
||||
if (toolMessage.result.type !== 'error') {
|
||||
if (toolMessage.result.type === 'success') {
|
||||
const { value, params } = toolMessage.result
|
||||
componentParams.numResults = value.uris.length
|
||||
componentParams.children = value.uris.length === 0 ? null : <>
|
||||
{value.uris.map((uri, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="hover:brightness-125 hover:cursor-pointer transition-all duration-200 flex items-center flex-nowrap"
|
||||
onClick={() => {
|
||||
commandService.executeCommand('vscode.open', uri, { preview: true })
|
||||
}}
|
||||
>
|
||||
<div className="flex-shrink-0"><svg className="w-1 h-1 opacity-60 mr-1.5 fill-current" viewBox="0 0 100 40"><rect x="0" y="15" width="100" height="10" /></svg></div>
|
||||
{uri.fsPath.split('/').pop()}
|
||||
</div>
|
||||
))}
|
||||
{value.hasNextPage && (
|
||||
<div className="italic">
|
||||
More results available...
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
componentParams.children = <ToolContentsWrapper>
|
||||
{value.uris.length === 0 ? <>
|
||||
<ListableToolItem name={'No results found.'} isSmall={true} />
|
||||
</> : <>
|
||||
{value.uris.map((uri, i) => (<ListableToolItem key={i}
|
||||
name={getBasename(uri.fsPath)}
|
||||
onClick={() => { commandService.executeCommand('vscode.open', uri, { preview: true }) }}
|
||||
/>))}
|
||||
{value.hasNextPage &&
|
||||
<ListableToolItem name={'Results truncated.'} isSmall={true} />
|
||||
}
|
||||
</>}
|
||||
</ToolContentsWrapper>
|
||||
}
|
||||
else {
|
||||
componentParams.children = <>
|
||||
|
|
@ -1251,30 +1263,31 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
resultWrapper: ({ toolMessage }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const title = toolNameToTitle[toolMessage.name]
|
||||
const title = toolNameToTitle[toolMessage.name].past
|
||||
const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params)
|
||||
const icon = null
|
||||
|
||||
if (toolMessage.result.type === 'rejected') return null
|
||||
if (toolMessage.result.type === 'rejected') return null // will never happen, not rejectable
|
||||
|
||||
const isError = toolMessage.result.type === 'error'
|
||||
const componentParams: ToolHeaderParams = { title, desc1, isError, icon }
|
||||
|
||||
if (toolMessage.result.type !== 'error') {
|
||||
if (toolMessage.result.type === 'success') {
|
||||
const { value, params } = toolMessage.result
|
||||
componentParams.numResults = value.uris.length
|
||||
componentParams.children = value.uris.length === 0 ? null : <>
|
||||
{value.uris.map((uri, i) => (
|
||||
<div key={i}
|
||||
className="hover:brightness-125 hover:cursor-pointer transition-all duration-200 flex items-center flex-nowrap"
|
||||
componentParams.children = <ToolContentsWrapper>
|
||||
{value.uris.length === 0 ? <>
|
||||
<ListableToolItem name={'No results found.'} isSmall={true} />
|
||||
</> : <>
|
||||
{value.uris.map((uri, i) => (<ListableToolItem key={i}
|
||||
name={getBasename(uri.fsPath)}
|
||||
onClick={() => { commandService.executeCommand('vscode.open', uri, { preview: true }) }}
|
||||
>
|
||||
<div className="flex-shrink-0"><svg className="w-1 h-1 opacity-60 mr-1.5 fill-current" viewBox="0 0 100 40"><rect x="0" y="15" width="100" height="10" /></svg></div>
|
||||
{uri.fsPath.split('/').pop()}
|
||||
</div>
|
||||
))}
|
||||
{value.hasNextPage && (<div className="italic">More results available...</div>)}
|
||||
</>
|
||||
/>))}
|
||||
{value.hasNextPage &&
|
||||
<ListableToolItem name={`Results truncated.`} isSmall={true} />
|
||||
}
|
||||
</>}
|
||||
</ToolContentsWrapper>
|
||||
}
|
||||
else {
|
||||
componentParams.children = <>
|
||||
|
|
@ -1291,7 +1304,7 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
requestWrapper: ({ toolRequest }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const title = toolNameToTitle[toolRequest.name]
|
||||
const title = toolNameToTitle[toolRequest.name].proposed
|
||||
const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params)
|
||||
const icon = null
|
||||
|
||||
|
|
@ -1306,7 +1319,7 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
resultWrapper: ({ toolMessage }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const title = toolNameToTitle[toolMessage.name]
|
||||
const title = toolNameToTitle[toolMessage.name].past
|
||||
const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params)
|
||||
const icon = null
|
||||
|
||||
|
|
@ -1335,7 +1348,7 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
requestWrapper: ({ toolRequest, }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const title = toolNameToTitle[toolRequest.name]
|
||||
const title = toolNameToTitle[toolRequest.name].proposed
|
||||
const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params)
|
||||
const icon = null
|
||||
|
||||
|
|
@ -1350,7 +1363,7 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
resultWrapper: ({ toolMessage }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const title = toolNameToTitle[toolMessage.name]
|
||||
const title = toolNameToTitle[toolMessage.name].past
|
||||
const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params)
|
||||
const icon = null
|
||||
|
||||
|
|
@ -1378,7 +1391,7 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
requestWrapper: ({ toolRequest, }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const title = toolNameToTitle[toolRequest.name]
|
||||
const title = toolNameToTitle[toolRequest.name].proposed
|
||||
const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params)
|
||||
const icon = null
|
||||
|
||||
|
|
@ -1386,21 +1399,22 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
const componentParams: ToolHeaderParams = { title, desc1, isError, icon, }
|
||||
|
||||
const { params } = toolRequest
|
||||
componentParams.children = <div>
|
||||
<div
|
||||
className=''
|
||||
onClick={() => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }}>
|
||||
{getBasename(params.uri.fsPath)}
|
||||
componentParams.children = <ToolContentsWrapper className='bg-void-bg-3'>
|
||||
<ListableToolItem
|
||||
name={getBasename(params.uri.fsPath)}
|
||||
onClick={() => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }}
|
||||
/>
|
||||
<div className='select-auto cursor-auto'>
|
||||
<ChatMarkdownRender string={params.changeDescription} codeURI={params.uri} chatMessageLocation={undefined} />
|
||||
</div>
|
||||
<ChatMarkdownRender string={params.changeDescription} chatMessageLocation={undefined} />
|
||||
</div>
|
||||
</ToolContentsWrapper>
|
||||
|
||||
return <ToolHeaderComponent {...componentParams} />
|
||||
},
|
||||
resultWrapper: ({ toolMessage }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const title = toolNameToTitle[toolMessage.name]
|
||||
const title = toolNameToTitle[toolMessage.name].past
|
||||
const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params)
|
||||
const icon = null
|
||||
|
||||
|
|
@ -1409,12 +1423,12 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
|
||||
if (toolMessage.result.type === 'success') {
|
||||
const { params } = toolMessage.result
|
||||
componentParams.children = <ChatMarkdownRender string={params.changeDescription} chatMessageLocation={undefined} />
|
||||
componentParams.children = <ChatMarkdownRender string={params.changeDescription} codeURI={params.uri} chatMessageLocation={undefined} />
|
||||
componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }
|
||||
}
|
||||
else if (toolMessage.result.type === 'rejected') {
|
||||
const { params } = toolMessage.result
|
||||
componentParams.children = <ChatMarkdownRender string={params.changeDescription} chatMessageLocation={undefined} />
|
||||
componentParams.children = <ChatMarkdownRender string={params.changeDescription} codeURI={params.uri} chatMessageLocation={undefined} />
|
||||
componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }
|
||||
}
|
||||
else if (toolMessage.result.type === 'error') {
|
||||
|
|
@ -1431,7 +1445,7 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const terminalToolsService = accessor.get('ITerminalToolService')
|
||||
const title = toolNameToTitle[toolRequest.name]
|
||||
const title = toolNameToTitle[toolRequest.name].proposed
|
||||
const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params)
|
||||
const icon = null
|
||||
|
||||
|
|
@ -1451,7 +1465,7 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const terminalToolsService = accessor.get('ITerminalToolService')
|
||||
const title = toolNameToTitle[toolMessage.name]
|
||||
const title = toolNameToTitle[toolMessage.name].past
|
||||
const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params)
|
||||
const icon = null
|
||||
|
||||
|
|
@ -1529,7 +1543,8 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx, isLast }: ChatBubblePr
|
|||
}
|
||||
else if (role === 'tool_request') {
|
||||
const ToolRequestWrapper = toolNameToComponent[chatMessage.name].requestWrapper as React.FC<{ toolRequest: any }> // ts isnt smart enough...
|
||||
if (!isLast) return null
|
||||
// if (!isLast) return null
|
||||
if (!ToolRequestWrapper) return null
|
||||
return <>
|
||||
<ToolRequestWrapper toolRequest={chatMessage} />
|
||||
<ToolRequestAcceptRejectButtons voidToolId={chatMessage.voidToolId} />
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ const directoryResultToString = (params: ToolCallParams['list_dir'], result: Too
|
|||
let output = '';
|
||||
const entries = result.children;
|
||||
|
||||
if (!result.hasPrevPage) {
|
||||
if (!result.hasPrevPage) { // is first page
|
||||
output += `${params.rootURI}\n`;
|
||||
}
|
||||
|
||||
|
|
@ -351,7 +351,7 @@ export class ToolsService implements IToolsService {
|
|||
},
|
||||
list_dir: (params, result) => {
|
||||
const dirTreeStr = directoryResultToString(params, result)
|
||||
return dirTreeStr + nextPageStr(result.hasNextPage)
|
||||
return dirTreeStr // + nextPageStr(result.hasNextPage) // already handles num results remaining
|
||||
},
|
||||
pathname_search: (params, result) => {
|
||||
return result.uris.map(uri => uri.fsPath).join('\n') + nextPageStr(result.hasNextPage)
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ import { CodeSelection, FileSelection, StagingSelectionItem } from '../chatThrea
|
|||
export const tripleTick = ['```', '```']
|
||||
|
||||
export const editToolDesc_toolDescription = `\
|
||||
A high level description of the change you'd like to make in the file. This description will be handed to a dumber, faster model that will quickly apply the change. \
|
||||
Typically the best description you can give here is a single code block of the form:\n${tripleTick[0]}\n// ... existing code ...\n{{change 1}}\n// ... existing code ...\n{{change2}}\n// ... existing code ...\n{{change 3}}\n...\n${tripleTick[1]}. \
|
||||
A high level description of the change you'd like to make in the file. This description will be handed to a dumber, faster model that will quickly apply the change.\
|
||||
The model does not have ANY context except the file content and this description, so make sure to include all necessary information to make the change here.\
|
||||
Typically the best description you can give is a code block of the form:\n${tripleTick[0]}\n// ... existing code ...\n{{change 1}}\n// ... existing code ...\n{{change2}}\n// ... existing code ...\n{{change 3}}\n...\n${tripleTick[1]}. \
|
||||
Do NOT output the whole file here if possible, and try to write as LITTLE code as needed to describe the change.`
|
||||
|
||||
|
||||
|
|
@ -29,22 +30,23 @@ The user's system information is as follows:
|
|||
- ${os}
|
||||
- Open workspace(s): ${workspaces.join(', ') || 'NO WORKSPACE OPEN'}
|
||||
${(mode === 'agent' || mode === 'gather') && runningTerminalIds.length !== 0 ? `\
|
||||
- Running terminal IDs: ${runningTerminalIds.join(', ')}
|
||||
- Existing terminal IDs: ${runningTerminalIds.join(', ')}
|
||||
`: '\n'}
|
||||
${mode === 'agent' || mode === 'gather' /* tool use */ ? `\
|
||||
You will be given tools you can call.
|
||||
- Only use tools if they help you accomplish the user's goal. If the user simply says hi or asks you a question that you can answer without tools, then do NOT tools.
|
||||
- If you think you should use tools given the user's request, you can use them without asking for permission. Feel free to use tools to gather context, understand the codebase, ${mode === 'agent' ? 'edit files, ' : ''}etc.
|
||||
- NEVER refer to a tool by name when speaking with the user. For example, do NOT say to the user "I'm going to use \`list_dir\`". Instead, say "I'm going to list all files in ___ directory", etc. Do not refer to "pages" of results, just say you're getting more results.
|
||||
- Some tools only work if the user has a workspace open. ${mode === 'gather' ? '' : `
|
||||
- NEVER modify a file outside one of the the user's workspaces without confirmation from the user.`}
|
||||
- Only use tools if they help you accomplish the user's goal. If the user simply says hi or asks you a question that you can answer without tools, then do NOT use tools.
|
||||
- If you think you should use tools, you do not need to ask for permission. Feel free to call tools whenever you'd like. You can use them to understand the codebase, ${mode === 'agent' ? 'run terminal commands, edit files, ' : 'gather relevant files and information, '}etc.
|
||||
- NEVER refer to a tool by name when speaking with the user (NEVER say something like "I'm going to use \`tool_name\`"). Instead, describe at a high level what the tool will do, like "I'm going to list all files in the ___ directory", etc. Also do not refer to "pages" of results, just say you're getting more results.
|
||||
- Some tools only work if the user has a workspace open.${mode === 'agent' ? `
|
||||
- NEVER modify a file outside the user's workspace(s) without permission from the user.` : ''}
|
||||
\
|
||||
`: `\
|
||||
You're allowed to ask for more context. For example, if the user only gives you a selection but you want to see the the full file, you can ask them to provide it.\
|
||||
`}
|
||||
|
||||
${mode === 'agent' /* code blocks */ ? `\
|
||||
If you have a change to make, you should almost always use a tool to edit the file. Even if you don't (e.g. if the user asks you not to), you should still NEVER re-write the entire file for the user. Instead, you should write comments like "// ... existing code" to indicate how to change the existing code. \
|
||||
- Prioritize editing files and running commands over simply making suggestions.
|
||||
- Prioritize taking as many steps as you need to complete your request over stopping early.\
|
||||
`: `\
|
||||
If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion in CODE BLOCK(S) (wrapped in triple backticks).
|
||||
- The first line of the code block must be the FULL PATH of the file you want to change. If the path does not already exist, it will be created.
|
||||
|
|
@ -53,8 +55,8 @@ If you think it's appropriate to suggest an edit to a file, then you must descri
|
|||
- Do NOT re-write the entire file in the code block(s). Instead, write comments like "// ... existing code" to indicate how to change the existing code.`}
|
||||
|
||||
Misc:
|
||||
- Always wrap any code you produce in triple backticks.
|
||||
\
|
||||
- Do not make things up.
|
||||
- Always wrap any code you produce in triple backticks, and specify a language if possible. For example, ${tripleTick[0]}typescript\n...\n${tripleTick[1]}.\
|
||||
`
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue