mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
refactor(vscode-ide-companion): 重构工具调用组件
- 重构 ExecuteToolCall、GenericToolCall、ReadToolCall 等组件 - 统一工具调用组件的展示样式和交互逻辑 - 优化代码结构,提高可维护性 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -8,63 +8,82 @@
|
|||||||
|
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import type { BaseToolCallProps } from './shared/types.js';
|
import type { BaseToolCallProps } from './shared/types.js';
|
||||||
import {
|
import { ToolCallCard, ToolCallRow } from './shared/LayoutComponents.js';
|
||||||
ToolCallCard,
|
import { safeTitle, groupContent } from './shared/utils.js';
|
||||||
ToolCallRow,
|
|
||||||
StatusIndicator,
|
|
||||||
CodeBlock,
|
|
||||||
} from './shared/LayoutComponents.js';
|
|
||||||
import { formatValue, safeTitle, groupContent } from './shared/utils.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized component for Execute tool calls
|
* Specialized component for Execute tool calls
|
||||||
* Optimized for displaying command execution with stdout/stderr
|
* Optimized for displaying command execution with stdout/stderr
|
||||||
|
* Shows command + output (if any) or error
|
||||||
*/
|
*/
|
||||||
export const ExecuteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
export const ExecuteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||||
const { title, status, rawInput, content } = toolCall;
|
const { title, content } = toolCall;
|
||||||
const titleText = safeTitle(title);
|
const commandText = safeTitle(title);
|
||||||
|
|
||||||
// Group content by type
|
// Group content by type
|
||||||
const { textOutputs, errors, otherData } = groupContent(content);
|
const { textOutputs, errors } = groupContent(content);
|
||||||
|
|
||||||
return (
|
// Error case: show command + error
|
||||||
<ToolCallCard icon="⚡">
|
if (errors.length > 0) {
|
||||||
{/* Title row */}
|
return (
|
||||||
<ToolCallRow label="Execute">
|
<ToolCallCard icon="⚡">
|
||||||
<StatusIndicator status={status} text={titleText} />
|
|
||||||
</ToolCallRow>
|
|
||||||
|
|
||||||
{/* Command */}
|
|
||||||
{rawInput && (
|
|
||||||
<ToolCallRow label="Command">
|
<ToolCallRow label="Command">
|
||||||
<CodeBlock>{formatValue(rawInput)}</CodeBlock>
|
<div style={{ fontFamily: 'var(--app-monospace-font-family)' }}>
|
||||||
</ToolCallRow>
|
{commandText}
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Standard output */}
|
|
||||||
{textOutputs.length > 0 && (
|
|
||||||
<ToolCallRow label="Output">
|
|
||||||
<CodeBlock>{textOutputs.join('\n')}</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Standard error / Errors */}
|
|
||||||
{errors.length > 0 && (
|
|
||||||
<ToolCallRow label="Error">
|
|
||||||
<div style={{ color: '#c74e39' }}>
|
|
||||||
<CodeBlock>{errors.join('\n')}</CodeBlock>
|
|
||||||
</div>
|
</div>
|
||||||
</ToolCallRow>
|
</ToolCallRow>
|
||||||
)}
|
<ToolCallRow label="Error">
|
||||||
|
<div
|
||||||
{/* Exit code or other execution details */}
|
style={{
|
||||||
{otherData.length > 0 && (
|
color: '#c74e39',
|
||||||
<ToolCallRow label="Details">
|
fontWeight: 500,
|
||||||
<CodeBlock>
|
whiteSpace: 'pre-wrap',
|
||||||
{otherData.map((data: unknown) => formatValue(data)).join('\n\n')}
|
}}
|
||||||
</CodeBlock>
|
>
|
||||||
|
{errors.join('\n')}
|
||||||
|
</div>
|
||||||
</ToolCallRow>
|
</ToolCallRow>
|
||||||
)}
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success with output: show command + output (limited)
|
||||||
|
if (textOutputs.length > 0) {
|
||||||
|
const output = textOutputs.join('\n');
|
||||||
|
const truncatedOutput =
|
||||||
|
output.length > 500 ? output.substring(0, 500) + '...' : output;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolCallCard icon="⚡">
|
||||||
|
<ToolCallRow label="Command">
|
||||||
|
<div style={{ fontFamily: 'var(--app-monospace-font-family)' }}>
|
||||||
|
{commandText}
|
||||||
|
</div>
|
||||||
|
</ToolCallRow>
|
||||||
|
<ToolCallRow label="Output">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontFamily: 'var(--app-monospace-font-family)',
|
||||||
|
fontSize: '13px',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
opacity: 0.9,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{truncatedOutput}
|
||||||
|
</div>
|
||||||
|
</ToolCallRow>
|
||||||
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success without output: show command only
|
||||||
|
return (
|
||||||
|
<ToolCallCard icon="⚡">
|
||||||
|
<ToolCallRow label="Executed">
|
||||||
|
<div style={{ fontFamily: 'var(--app-monospace-font-family)' }}>
|
||||||
|
{commandText}
|
||||||
|
</div>
|
||||||
|
</ToolCallRow>
|
||||||
</ToolCallCard>
|
</ToolCallCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,86 +11,114 @@ import type { BaseToolCallProps } from './shared/types.js';
|
|||||||
import {
|
import {
|
||||||
ToolCallCard,
|
ToolCallCard,
|
||||||
ToolCallRow,
|
ToolCallRow,
|
||||||
StatusIndicator,
|
|
||||||
CodeBlock,
|
|
||||||
LocationsList,
|
LocationsList,
|
||||||
} from './shared/LayoutComponents.js';
|
} from './shared/LayoutComponents.js';
|
||||||
import { DiffDisplay } from './shared/DiffDisplay.js';
|
import { DiffDisplay } from './shared/DiffDisplay.js';
|
||||||
import {
|
import { safeTitle, groupContent } from './shared/utils.js';
|
||||||
formatValue,
|
import { useVSCode } from '../../hooks/useVSCode.js';
|
||||||
safeTitle,
|
|
||||||
getKindIcon,
|
|
||||||
groupContent,
|
|
||||||
} from './shared/utils.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic tool call component that can display any tool call type
|
* Generic tool call component that can display any tool call type
|
||||||
* Used as fallback for unknown tool call kinds
|
* Used as fallback for unknown tool call kinds
|
||||||
|
* Minimal display: show description and outcome
|
||||||
*/
|
*/
|
||||||
export const GenericToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
export const GenericToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||||
const { kind, title, status, rawInput, content, locations } = toolCall;
|
const { kind, title, content, locations } = toolCall;
|
||||||
const kindIcon = getKindIcon(kind);
|
const operationText = safeTitle(title);
|
||||||
const titleText = safeTitle(title);
|
const vscode = useVSCode();
|
||||||
|
|
||||||
// Group content by type
|
// Group content by type
|
||||||
const { textOutputs, errors, diffs, otherData } = groupContent(content);
|
const { textOutputs, errors, diffs } = groupContent(content);
|
||||||
|
|
||||||
return (
|
const handleOpenDiff = (
|
||||||
<ToolCallCard icon={kindIcon}>
|
path: string | undefined,
|
||||||
{/* Title row */}
|
oldText: string | null | undefined,
|
||||||
<ToolCallRow label="Tool">
|
newText: string | undefined,
|
||||||
<StatusIndicator status={status} text={titleText} />
|
) => {
|
||||||
</ToolCallRow>
|
if (path) {
|
||||||
|
vscode.postMessage({
|
||||||
|
type: 'openDiff',
|
||||||
|
data: { path, oldText: oldText || '', newText: newText || '' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
{/* Input row */}
|
// Error case: show operation + error
|
||||||
{rawInput && (
|
if (errors.length > 0) {
|
||||||
<ToolCallRow label="Input">
|
return (
|
||||||
<CodeBlock>{formatValue(rawInput)}</CodeBlock>
|
<ToolCallCard icon="🔧">
|
||||||
|
<ToolCallRow label={kind}>
|
||||||
|
<div>{operationText}</div>
|
||||||
</ToolCallRow>
|
</ToolCallRow>
|
||||||
)}
|
<ToolCallRow label="Error">
|
||||||
|
<div style={{ color: '#c74e39', fontWeight: 500 }}>
|
||||||
|
{errors.join('\n')}
|
||||||
|
</div>
|
||||||
|
</ToolCallRow>
|
||||||
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* Locations row */}
|
// Success with diff: show diff
|
||||||
{locations && locations.length > 0 && (
|
if (diffs.length > 0) {
|
||||||
<ToolCallRow label="Files">
|
return (
|
||||||
|
<ToolCallCard icon="🔧">
|
||||||
|
{diffs.map(
|
||||||
|
(item: import('./shared/types.js').ToolCallContent, idx: number) => (
|
||||||
|
<div key={`diff-${idx}`} style={{ gridColumn: '1 / -1' }}>
|
||||||
|
<DiffDisplay
|
||||||
|
path={item.path}
|
||||||
|
oldText={item.oldText}
|
||||||
|
newText={item.newText}
|
||||||
|
onOpenDiff={() =>
|
||||||
|
handleOpenDiff(item.path, item.oldText, item.newText)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success with output: show operation + output (truncated)
|
||||||
|
if (textOutputs.length > 0) {
|
||||||
|
const output = textOutputs.join('\n');
|
||||||
|
const truncatedOutput =
|
||||||
|
output.length > 300 ? output.substring(0, 300) + '...' : output;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolCallCard icon="🔧">
|
||||||
|
<ToolCallRow label={kind}>
|
||||||
|
<div>{operationText}</div>
|
||||||
|
</ToolCallRow>
|
||||||
|
<ToolCallRow label="Output">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
fontFamily: 'var(--app-monospace-font-family)',
|
||||||
|
fontSize: '13px',
|
||||||
|
opacity: 0.9,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{truncatedOutput}
|
||||||
|
</div>
|
||||||
|
</ToolCallRow>
|
||||||
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success with files: show operation + file list
|
||||||
|
if (locations && locations.length > 0) {
|
||||||
|
return (
|
||||||
|
<ToolCallCard icon="🔧">
|
||||||
|
<ToolCallRow label={kind}>
|
||||||
<LocationsList locations={locations} />
|
<LocationsList locations={locations} />
|
||||||
</ToolCallRow>
|
</ToolCallRow>
|
||||||
)}
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* Output row - combined text outputs */}
|
// No output
|
||||||
{textOutputs.length > 0 && (
|
return null;
|
||||||
<ToolCallRow label="Output">
|
|
||||||
<CodeBlock>{textOutputs.join('\n')}</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Error row - combined errors */}
|
|
||||||
{errors.length > 0 && (
|
|
||||||
<ToolCallRow label="Error">
|
|
||||||
<div style={{ color: '#c74e39' }}>{errors.join('\n')}</div>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Diff rows */}
|
|
||||||
{diffs.map(
|
|
||||||
(item: import('./shared/types.js').ToolCallContent, idx: number) => (
|
|
||||||
<ToolCallRow key={`diff-${idx}`} label="Diff">
|
|
||||||
<DiffDisplay
|
|
||||||
path={item.path}
|
|
||||||
oldText={item.oldText}
|
|
||||||
newText={item.newText}
|
|
||||||
/>
|
|
||||||
</ToolCallRow>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Other data rows */}
|
|
||||||
{otherData.length > 0 && (
|
|
||||||
<ToolCallRow label="Data">
|
|
||||||
<CodeBlock>
|
|
||||||
{otherData.map((data: unknown) => formatValue(data)).join('\n\n')}
|
|
||||||
</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
</ToolCallCard>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,66 +11,45 @@ import type { BaseToolCallProps } from './shared/types.js';
|
|||||||
import {
|
import {
|
||||||
ToolCallCard,
|
ToolCallCard,
|
||||||
ToolCallRow,
|
ToolCallRow,
|
||||||
StatusIndicator,
|
|
||||||
CodeBlock,
|
|
||||||
LocationsList,
|
LocationsList,
|
||||||
} from './shared/LayoutComponents.js';
|
} from './shared/LayoutComponents.js';
|
||||||
import { formatValue, safeTitle, groupContent } from './shared/utils.js';
|
import { groupContent } from './shared/utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized component for Read tool calls
|
* Specialized component for Read tool calls
|
||||||
* Optimized for displaying file reading operations
|
* Optimized for displaying file reading operations
|
||||||
|
* Minimal display: just show file name, hide content (too verbose)
|
||||||
*/
|
*/
|
||||||
export const ReadToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
export const ReadToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||||
const { title, status, rawInput, content, locations } = toolCall;
|
const { content, locations } = toolCall;
|
||||||
const titleText = safeTitle(title);
|
|
||||||
|
|
||||||
// Group content by type
|
// Group content by type
|
||||||
const { textOutputs, errors, otherData } = groupContent(content);
|
const { errors } = groupContent(content);
|
||||||
|
|
||||||
return (
|
// Error case: show error with operation label
|
||||||
<ToolCallCard icon="📖">
|
if (errors.length > 0) {
|
||||||
{/* Title row */}
|
return (
|
||||||
<ToolCallRow label="Read">
|
<ToolCallCard icon="📖">
|
||||||
<StatusIndicator status={status} text={titleText} />
|
<ToolCallRow label="Read">
|
||||||
</ToolCallRow>
|
<div style={{ color: '#c74e39', fontWeight: 500 }}>
|
||||||
|
{errors.join('\n')}
|
||||||
|
</div>
|
||||||
|
</ToolCallRow>
|
||||||
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* File path(s) */}
|
// Success case: show which file was read
|
||||||
{locations && locations.length > 0 && (
|
if (locations && locations.length > 0) {
|
||||||
<ToolCallRow label="File">
|
return (
|
||||||
|
<ToolCallCard icon="📖">
|
||||||
|
<ToolCallRow label="Read">
|
||||||
<LocationsList locations={locations} />
|
<LocationsList locations={locations} />
|
||||||
</ToolCallRow>
|
</ToolCallRow>
|
||||||
)}
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* Input parameters (e.g., line range, offset) */}
|
// No file info, don't show
|
||||||
{rawInput && (
|
return null;
|
||||||
<ToolCallRow label="Options">
|
|
||||||
<CodeBlock>{formatValue(rawInput)}</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* File content output */}
|
|
||||||
{textOutputs.length > 0 && (
|
|
||||||
<ToolCallRow label="Content">
|
|
||||||
<CodeBlock>{textOutputs.join('\n')}</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Error handling */}
|
|
||||||
{errors.length > 0 && (
|
|
||||||
<ToolCallRow label="Error">
|
|
||||||
<div style={{ color: '#c74e39' }}>{errors.join('\n')}</div>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Other data */}
|
|
||||||
{otherData.length > 0 && (
|
|
||||||
<ToolCallRow label="Details">
|
|
||||||
<CodeBlock>
|
|
||||||
{otherData.map((data: unknown) => formatValue(data)).join('\n\n')}
|
|
||||||
</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
</ToolCallCard>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,66 +11,56 @@ import type { BaseToolCallProps } from './shared/types.js';
|
|||||||
import {
|
import {
|
||||||
ToolCallCard,
|
ToolCallCard,
|
||||||
ToolCallRow,
|
ToolCallRow,
|
||||||
StatusIndicator,
|
|
||||||
CodeBlock,
|
|
||||||
LocationsList,
|
LocationsList,
|
||||||
} from './shared/LayoutComponents.js';
|
} from './shared/LayoutComponents.js';
|
||||||
import { formatValue, safeTitle, groupContent } from './shared/utils.js';
|
import { safeTitle, groupContent } from './shared/utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized component for Search tool calls
|
* Specialized component for Search tool calls
|
||||||
* Optimized for displaying search operations and results
|
* Optimized for displaying search operations and results
|
||||||
|
* Shows query + result count or file list
|
||||||
*/
|
*/
|
||||||
export const SearchToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
export const SearchToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||||
const { title, status, rawInput, content, locations } = toolCall;
|
const { title, content, locations } = toolCall;
|
||||||
const titleText = safeTitle(title);
|
const queryText = safeTitle(title);
|
||||||
|
|
||||||
// Group content by type
|
// Group content by type
|
||||||
const { textOutputs, errors, otherData } = groupContent(content);
|
const { errors } = groupContent(content);
|
||||||
|
|
||||||
return (
|
// Error case: show search query + error
|
||||||
<ToolCallCard icon="🔍">
|
if (errors.length > 0) {
|
||||||
{/* Title row */}
|
return (
|
||||||
<ToolCallRow label="Search">
|
<ToolCallCard icon="🔍">
|
||||||
<StatusIndicator status={status} text={titleText} />
|
<ToolCallRow label="Search">
|
||||||
</ToolCallRow>
|
<div style={{ fontFamily: 'var(--app-monospace-font-family)' }}>
|
||||||
|
{queryText}
|
||||||
{/* Search query/pattern */}
|
</div>
|
||||||
{rawInput && (
|
|
||||||
<ToolCallRow label="Query">
|
|
||||||
<CodeBlock>{formatValue(rawInput)}</CodeBlock>
|
|
||||||
</ToolCallRow>
|
</ToolCallRow>
|
||||||
)}
|
<ToolCallRow label="Error">
|
||||||
|
<div style={{ color: '#c74e39', fontWeight: 500 }}>
|
||||||
|
{errors.join('\n')}
|
||||||
|
</div>
|
||||||
|
</ToolCallRow>
|
||||||
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* Search results - files found */}
|
// Success case with results: show search query + file list
|
||||||
{locations && locations.length > 0 && (
|
if (locations && locations.length > 0) {
|
||||||
<ToolCallRow label="Results">
|
return (
|
||||||
|
<ToolCallCard icon="🔍">
|
||||||
|
<ToolCallRow label="Search">
|
||||||
|
<div style={{ fontFamily: 'var(--app-monospace-font-family)' }}>
|
||||||
|
{queryText}
|
||||||
|
</div>
|
||||||
|
</ToolCallRow>
|
||||||
|
<ToolCallRow label={`Found (${locations.length})`}>
|
||||||
<LocationsList locations={locations} />
|
<LocationsList locations={locations} />
|
||||||
</ToolCallRow>
|
</ToolCallRow>
|
||||||
)}
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* Search output details */}
|
// No results
|
||||||
{textOutputs.length > 0 && (
|
return null;
|
||||||
<ToolCallRow label="Matches">
|
|
||||||
<CodeBlock>{textOutputs.join('\n')}</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Error handling */}
|
|
||||||
{errors.length > 0 && (
|
|
||||||
<ToolCallRow label="Error">
|
|
||||||
<div style={{ color: '#c74e39' }}>{errors.join('\n')}</div>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Other search metadata */}
|
|
||||||
{otherData.length > 0 && (
|
|
||||||
<ToolCallRow label="Details">
|
|
||||||
<CodeBlock>
|
|
||||||
{otherData.map((data: unknown) => formatValue(data)).join('\n\n')}
|
|
||||||
</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
</ToolCallCard>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,63 +8,56 @@
|
|||||||
|
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import type { BaseToolCallProps } from './shared/types.js';
|
import type { BaseToolCallProps } from './shared/types.js';
|
||||||
import {
|
import { ToolCallCard, ToolCallRow } from './shared/LayoutComponents.js';
|
||||||
ToolCallCard,
|
import { groupContent } from './shared/utils.js';
|
||||||
ToolCallRow,
|
|
||||||
StatusIndicator,
|
|
||||||
CodeBlock,
|
|
||||||
} from './shared/LayoutComponents.js';
|
|
||||||
import { formatValue, safeTitle, groupContent } from './shared/utils.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized component for Think tool calls
|
* Specialized component for Think tool calls
|
||||||
* Optimized for displaying AI reasoning and thought processes
|
* Optimized for displaying AI reasoning and thought processes
|
||||||
|
* Minimal display: just show the thoughts (no context)
|
||||||
*/
|
*/
|
||||||
export const ThinkToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
export const ThinkToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||||
const { title, status, rawInput, content } = toolCall;
|
const { content } = toolCall;
|
||||||
const titleText = safeTitle(title);
|
|
||||||
|
|
||||||
// Group content by type
|
// Group content by type
|
||||||
const { textOutputs, errors, otherData } = groupContent(content);
|
const { textOutputs, errors } = groupContent(content);
|
||||||
|
|
||||||
return (
|
// Error case (rare for thinking)
|
||||||
<ToolCallCard icon="💭">
|
if (errors.length > 0) {
|
||||||
{/* Title row */}
|
return (
|
||||||
<ToolCallRow label="Thinking">
|
<ToolCallCard icon="💭">
|
||||||
<StatusIndicator status={status} text={titleText} />
|
<ToolCallRow label="Error">
|
||||||
</ToolCallRow>
|
<div style={{ color: '#c74e39', fontWeight: 500 }}>
|
||||||
|
{errors.join('\n')}
|
||||||
{/* Thinking context/prompt */}
|
|
||||||
{rawInput && (
|
|
||||||
<ToolCallRow label="Context">
|
|
||||||
<CodeBlock>{formatValue(rawInput)}</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Thought content */}
|
|
||||||
{textOutputs.length > 0 && (
|
|
||||||
<ToolCallRow label="Thoughts">
|
|
||||||
<div style={{ fontStyle: 'italic', opacity: 0.95 }}>
|
|
||||||
{textOutputs.join('\n\n')}
|
|
||||||
</div>
|
</div>
|
||||||
</ToolCallRow>
|
</ToolCallRow>
|
||||||
)}
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* Error handling */}
|
// Show thoughts with label
|
||||||
{errors.length > 0 && (
|
if (textOutputs.length > 0) {
|
||||||
<ToolCallRow label="Error">
|
const thoughts = textOutputs.join('\n\n');
|
||||||
<div style={{ color: '#c74e39' }}>{errors.join('\n')}</div>
|
const truncatedThoughts =
|
||||||
</ToolCallRow>
|
thoughts.length > 500 ? thoughts.substring(0, 500) + '...' : thoughts;
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Other reasoning data */}
|
return (
|
||||||
{otherData.length > 0 && (
|
<ToolCallCard icon="💭">
|
||||||
<ToolCallRow label="Details">
|
<ToolCallRow label="Thinking">
|
||||||
<CodeBlock>
|
<div
|
||||||
{otherData.map((data: unknown) => formatValue(data)).join('\n\n')}
|
style={{
|
||||||
</CodeBlock>
|
fontStyle: 'italic',
|
||||||
|
opacity: 0.9,
|
||||||
|
lineHeight: 1.6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{truncatedThoughts}
|
||||||
|
</div>
|
||||||
</ToolCallRow>
|
</ToolCallRow>
|
||||||
)}
|
</ToolCallCard>
|
||||||
</ToolCallCard>
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
// Empty thoughts
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,26 +11,24 @@ import type { BaseToolCallProps } from './shared/types.js';
|
|||||||
import {
|
import {
|
||||||
ToolCallCard,
|
ToolCallCard,
|
||||||
ToolCallRow,
|
ToolCallRow,
|
||||||
StatusIndicator,
|
|
||||||
CodeBlock,
|
|
||||||
LocationsList,
|
LocationsList,
|
||||||
} from './shared/LayoutComponents.js';
|
} from './shared/LayoutComponents.js';
|
||||||
import { DiffDisplay } from './shared/DiffDisplay.js';
|
import { DiffDisplay } from './shared/DiffDisplay.js';
|
||||||
import { formatValue, safeTitle, groupContent } from './shared/utils.js';
|
import { groupContent } from './shared/utils.js';
|
||||||
import { useVSCode } from '../../hooks/useVSCode.js';
|
import { useVSCode } from '../../hooks/useVSCode.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized component for Write/Edit tool calls
|
* Specialized component for Write/Edit tool calls
|
||||||
* Optimized for displaying file writing and editing operations with diffs
|
* Optimized for displaying file writing and editing operations with diffs
|
||||||
|
* Follows minimal display principle: only show what matters
|
||||||
*/
|
*/
|
||||||
export const WriteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
export const WriteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
||||||
const { kind, title, status, rawInput, content, locations } = toolCall;
|
const { kind, status: _status, content, locations } = toolCall;
|
||||||
const titleText = safeTitle(title);
|
|
||||||
const isEdit = kind.toLowerCase() === 'edit';
|
const isEdit = kind.toLowerCase() === 'edit';
|
||||||
const vscode = useVSCode();
|
const vscode = useVSCode();
|
||||||
|
|
||||||
// Group content by type
|
// Group content by type
|
||||||
const { textOutputs, errors, diffs, otherData } = groupContent(content);
|
const { errors, diffs } = groupContent(content);
|
||||||
|
|
||||||
const handleOpenDiff = (
|
const handleOpenDiff = (
|
||||||
path: string | undefined,
|
path: string | undefined,
|
||||||
@@ -45,65 +43,52 @@ export const WriteToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
// Error case: show error with operation label
|
||||||
<ToolCallCard icon="✏️">
|
if (errors.length > 0) {
|
||||||
{/* Title row */}
|
return (
|
||||||
<ToolCallRow label={isEdit ? 'Edit' : 'Write'}>
|
<ToolCallCard icon="✏️">
|
||||||
<StatusIndicator status={status} text={titleText} />
|
<ToolCallRow label={isEdit ? 'Edit' : 'Write'}>
|
||||||
</ToolCallRow>
|
<div style={{ color: '#c74e39', fontWeight: 500 }}>
|
||||||
|
{errors.join('\n')}
|
||||||
|
</div>
|
||||||
|
</ToolCallRow>
|
||||||
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* File path(s) */}
|
// Success case with diff: show diff (already has file path)
|
||||||
{locations && locations.length > 0 && (
|
if (diffs.length > 0) {
|
||||||
<ToolCallRow label="File">
|
return (
|
||||||
|
<ToolCallCard icon="✏️">
|
||||||
|
{diffs.map(
|
||||||
|
(item: import('./shared/types.js').ToolCallContent, idx: number) => (
|
||||||
|
<div key={`diff-${idx}`} style={{ gridColumn: '1 / -1' }}>
|
||||||
|
<DiffDisplay
|
||||||
|
path={item.path}
|
||||||
|
oldText={item.oldText}
|
||||||
|
newText={item.newText}
|
||||||
|
onOpenDiff={() =>
|
||||||
|
handleOpenDiff(item.path, item.oldText, item.newText)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success case without diff: show operation + file
|
||||||
|
if (locations && locations.length > 0) {
|
||||||
|
return (
|
||||||
|
<ToolCallCard icon="✏️">
|
||||||
|
<ToolCallRow label={isEdit ? 'Edited' : 'Created'}>
|
||||||
<LocationsList locations={locations} />
|
<LocationsList locations={locations} />
|
||||||
</ToolCallRow>
|
</ToolCallRow>
|
||||||
)}
|
</ToolCallCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* Input parameters (e.g., old_string, new_string for edits) */}
|
// No output, don't show anything
|
||||||
{rawInput && (
|
return null;
|
||||||
<ToolCallRow label="Changes">
|
|
||||||
<CodeBlock>{formatValue(rawInput)}</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Diff display - most important for write/edit operations */}
|
|
||||||
{diffs.map(
|
|
||||||
(item: import('./shared/types.js').ToolCallContent, idx: number) => (
|
|
||||||
<ToolCallRow key={`diff-${idx}`} label="Diff">
|
|
||||||
<DiffDisplay
|
|
||||||
path={item.path}
|
|
||||||
oldText={item.oldText}
|
|
||||||
newText={item.newText}
|
|
||||||
onOpenDiff={() =>
|
|
||||||
handleOpenDiff(item.path, item.oldText, item.newText)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ToolCallRow>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Success message or output */}
|
|
||||||
{textOutputs.length > 0 && (
|
|
||||||
<ToolCallRow label="Result">
|
|
||||||
<CodeBlock>{textOutputs.join('\n')}</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Error handling */}
|
|
||||||
{errors.length > 0 && (
|
|
||||||
<ToolCallRow label="Error">
|
|
||||||
<div style={{ color: '#c74e39' }}>{errors.join('\n')}</div>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Other data */}
|
|
||||||
{otherData.length > 0 && (
|
|
||||||
<ToolCallRow label="Details">
|
|
||||||
<CodeBlock>
|
|
||||||
{otherData.map((data: unknown) => formatValue(data)).join('\n\n')}
|
|
||||||
</CodeBlock>
|
|
||||||
</ToolCallRow>
|
|
||||||
)}
|
|
||||||
</ToolCallCard>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user