add download chat buttons

This commit is contained in:
Andrew Pareles 2025-04-16 18:09:03 -07:00
parent d99d2cca4a
commit 664428c8f0
3 changed files with 53 additions and 10 deletions

View file

@ -69,7 +69,7 @@ export const IconShell1 = ({ onClick, Icon, disabled, className, ...props }: Ico
const COPY_FEEDBACK_TIMEOUT = 1500 // amount of time to say 'Copied!'
export const CopyButton = ({ codeStr }: { codeStr: string }) => {
export const CopyButton = ({ codeStr, toolTipName }: { codeStr: string | (() => Promise<string> | string), toolTipName: string }) => {
const accessor = useAccessor()
const metricsService = accessor.get('IMetricsService')
@ -83,8 +83,8 @@ export const CopyButton = ({ codeStr }: { codeStr: string }) => {
}, COPY_FEEDBACK_TIMEOUT)
}, [copyButtonText])
const onCopy = useCallback(() => {
clipboardService.writeText(codeStr)
const onCopy = useCallback(async () => {
clipboardService.writeText(typeof codeStr === 'string' ? codeStr : await codeStr())
.then(() => { setCopyButtonText(CopyButtonText.Copied) })
.catch(() => { setCopyButtonText(CopyButtonText.Error) })
metricsService.capture('Copy Code', { length: codeStr.length }) // capture the length only
@ -93,7 +93,7 @@ export const CopyButton = ({ codeStr }: { codeStr: string }) => {
return <IconShell1
Icon={copyButtonText === CopyButtonText.Copied ? Check : copyButtonText === CopyButtonText.Error ? X : Copy}
onClick={onCopy}
{...tooltipPropsForApplyBlock({ tooltipName: 'Copy' })}
{...tooltipPropsForApplyBlock({ tooltipName: toolTipName })}
/>
}
@ -374,7 +374,7 @@ export const BlockCodeApplyWrapper = ({
</div>
<div className={`${canApply ? '' : 'hidden'} flex items-center gap-1`}>
<JumpToFileButton uri={uri} />
{currStreamState === 'idle-no-changes' && <CopyButton codeStr={initValue} />}
{currStreamState === 'idle-no-changes' && <CopyButton codeStr={initValue} toolTipName='Copy' />}
<ApplyButtonsHTML uri={uri} applyBoxId={applyBoxId} codeStr={initValue} reapplyIcon={false} />
</div>
</div>

View file

@ -17,10 +17,10 @@ import { ModelDropdown, } from '../void-settings-tsx/ModelDropdown.js';
import { SidebarThreadSelector } from './SidebarThreadSelector.js';
import { VOID_CTRL_L_ACTION_ID } from '../../../actionIDs.js';
import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js';
import { ChatMode, FeatureName, isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js';
import { ChatMode, displayInfoOfProviderName, FeatureName, isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js';
import { WarningBox } from '../void-settings-tsx/WarningBox.js';
import { getModelCapabilities, getIsReasoningEnabledState } from '../../../../common/modelCapabilities.js';
import { AlertTriangle, Ban, Check, ChevronRight, Dot, FileIcon, Pencil, Undo, Undo2, X, Flag } from 'lucide-react';
import { AlertTriangle, Ban, Check, ChevronRight, Dot, FileIcon, Pencil, Undo, Undo2, X, Flag, Copy as CopyIcon } from 'lucide-react';
import { ChatMessage, CheckpointEntry, StagingSelectionItem, ToolMessage } from '../../../../common/chatThreadServiceTypes.js';
import { LintErrorItem, ToolCallParams, ToolNameWithApproval } from '../../../../common/toolsServiceTypes.js';
import { ApplyButtonsHTML, CopyButton, IconShell1, JumpToFileButton, JumpToTerminalButton, StatusIndicator, StatusIndicatorForApplyButton, useApplyButtonState } from '../markdown/ApplyBlockHoverButtons.js';
@ -1390,7 +1390,7 @@ const EditToolHeaderButtons = ({ applyBoxId, uri, codeStr }: { applyBoxId: strin
<StatusIndicatorForApplyButton applyBoxId={applyBoxId} uri={uri} />
<JumpToFileButton uri={uri} />
{currStreamState === 'idle-no-changes' && <CopyButton codeStr={codeStr} />}
{currStreamState === 'idle-no-changes' && <CopyButton codeStr={codeStr} toolTipName='Copy' />}
<ApplyButtonsHTML applyBoxId={applyBoxId} uri={uri} codeStr={codeStr} reapplyIcon={true} />
</div>
}
@ -1578,11 +1578,12 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
const componentParams: ToolHeaderParams = { title, desc1, isError, icon }
if (toolMessage.type === 'success') {
const { params, result } = toolMessage
const { params, result, rawParams } = toolMessage
componentParams.numResults = result.uris.length
componentParams.hasNextPage = result.hasNextPage
componentParams.children = result.uris.length === 0 ? undefined
: <ToolChildrenWrapper>
{rawParams.search_in_folder ? `Search in ${rawParams.search_in_folder}` : null}
{result.uris.map((uri, i) => (<ListableToolItem key={i}
name={getBasename(uri.fsPath)}
className='w-full overflow-auto'
@ -1622,11 +1623,12 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
const componentParams: ToolHeaderParams = { title, desc1, isError, icon }
if (toolMessage.type === 'success') {
const { params, result } = toolMessage
const { params, result, rawParams } = toolMessage
componentParams.numResults = result.uris.length
componentParams.hasNextPage = result.hasNextPage
componentParams.children = result.uris.length === 0 ? undefined
: <ToolChildrenWrapper>
{rawParams.search_in_folder ? `Search in ${rawParams.search_in_folder}` : null}
{result.uris.map((uri, i) => (<ListableToolItem key={i}
name={getBasename(uri.fsPath)}
className='w-full overflow-auto'
@ -2121,9 +2123,48 @@ const CommandBarInChat = () => {
const accessor = useAccessor()
const editCodeService = accessor.get('IEditCodeService')
const commandService = accessor.get('ICommandService')
const chatThreadsService = accessor.get('IChatThreadService')
const chatThreadsState = useChatThreadsState()
const chatThreadsStreamState = useChatThreadsStreamState(chatThreadsState.currentThreadId)
const settingsState = useSettingsState()
const convertService = accessor.get('IConvertToLLMMessageService')
const currentThread = chatThreadsService.getCurrentThread()
const chatMode = settingsState.globalSettings.chatMode
const modelSelection = settingsState.modelSelectionOfFeature?.Chat ?? null
const copyChatButton = <CopyButton
codeStr={async () => {
const { messages } = await convertService.prepareLLMChatMessages({
chatMessages: currentThread.messages,
chatMode,
modelSelection,
})
return JSON.stringify(messages, null, 2)
}}
toolTipName={modelSelection === null ? 'Copy As Messages Payload' : `Copy As ${displayInfoOfProviderName(modelSelection.providerName).title} Payload`}
/>
const copyChatButton2 = <CopyButton
codeStr={async () => {
return JSON.stringify(currentThread.messages, null, 2)
}}
toolTipName={`Copy As Void Chat`}
/>
// (
// <IconShell1
// Icon={CopyIcon}
// onClick={copyChatToClipboard}
// data-tooltip-id='void-tooltip'
// data-tooltip-place='top'
// data-tooltip-content='Copy chat JSON'
// />
// )
const [fileDetailsOpenedState, setFileDetailsOpenedState] = useState<'auto-opened' | 'auto-closed' | 'user-opened' | 'user-closed'>('auto-closed');
const isFileDetailsOpened = fileDetailsOpenedState === 'auto-opened' || fileDetailsOpenedState === 'user-opened';

View file

@ -48,6 +48,7 @@ import { IVoidCommandBarService } from '../../../voidCommandBarService.js'
import { INativeHostService } from '../../../../../../../platform/native/common/native.js';
import { IEditCodeService } from '../../../editCodeServiceInterface.js'
import { IToolsService } from '../../../toolsService.js'
import { IConvertToLLMMessageService } from '../../../convertToLLMMessageService.js'
// normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes
@ -217,6 +218,7 @@ const getReactAccessor = (accessor: ServicesAccessor) => {
IVoidCommandBarService: accessor.get(IVoidCommandBarService),
INativeHostService: accessor.get(INativeHostService),
IToolsService: accessor.get(IToolsService),
IConvertToLLMMessageService: accessor.get(IConvertToLLMMessageService),
} as const
return reactAccessor