@@ -249,8 +251,14 @@ const toggleExpand = (groupId: string) => {
}
};
-const getStepIcon = (step: Step) => {
- return step.type === 'thinking' ? getIconPath('brain') : getIconPath('terminal');
+const getStepIconStyle = (step: Step) => {
+ if (step.type === 'thinking') {
+ return props.iconStyle ? props.iconStyle('brain') : {};
+ } else if (step.type === 'tool' && step.action.tool) {
+ const iconKey = props.getToolIcon ? props.getToolIcon(step.action.tool) : 'terminal';
+ return props.iconStyle ? props.iconStyle(iconKey) : {};
+ }
+ return props.iconStyle ? props.iconStyle('terminal') : {};
};
const getToolName = (action: Action) => {
@@ -322,15 +330,6 @@ const escapeHtml = (text: string) => {
return div.innerHTML;
};
-const getIconPath = (name: string) => {
- const icons: Record
= {
- brain: '',
- terminal: '',
- search: ''
- };
- return icons[name] || icons.brain;
-};
-
watch(() => props.actions, () => {
// 简化:不需要动态更新高度
}, { deep: true });
@@ -465,7 +464,6 @@ watch(() => props.actions, () => {
}
.step-icon {
- color: var(--claude-text-secondary);
flex-shrink: 0;
}
diff --git a/static/src/components/chat/actions/ToolAction.vue b/static/src/components/chat/actions/ToolAction.vue
index c123aeb..7caaee7 100644
--- a/static/src/components/chat/actions/ToolAction.vue
+++ b/static/src/components/chat/actions/ToolAction.vue
@@ -244,27 +244,49 @@ function renderCreateFile(result: any, args: any): string {
}
function renderWriteFile(result: any, args: any): string {
- const path = args.path || result.path || '';
+ const path = args.file_path || args.path || result.path || '';
const status = result.success ? '✓ 已写入' : '✗ 失败';
+ const content = args.content || '';
+ const isAppend = args.append || false;
let html = '';
+ // 显示写入的内容
+ if (content) {
+ html += '';
+ }
+
return html;
}
function renderEditFile(result: any, args: any): string {
- const path = args.path || result.path || '';
+ const path = args.file_path || args.path || result.path || '';
const status = result.success ? '✓ 已编辑' : '✗ 失败';
+
+ // 兼容两种数据格式:
+ // 1. 新格式:args.replacements 数组
+ // 2. 旧格式:args.old_string 和 args.new_string
const replacements = args.replacements || [];
+ let oldString = args.old_string || '';
+ let newString = args.new_string || '';
let html = '';
+ // 如果有 replacements 数组,使用新格式
if (replacements.length > 0) {
html += '';
replacements.forEach((rep: any, idx: number) => {
@@ -289,6 +311,30 @@ function renderEditFile(result: any, args: any): string {
});
html += '
';
}
+ // 否则使用旧格式(old_string/new_string)
+ else if (oldString || newString) {
+ // 处理转义的 \n
+ oldString = oldString.replace(/\\n/g, '\n');
+ newString = newString.replace(/\\n/g, '\n');
+
+ html += '';
+ }
return html;
}
@@ -324,7 +370,7 @@ function renderReadFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const size = result.size || result.file_size || 0;
- const content = result.content || result.text || '';
+ const readType = result.type || 'read';
let html = '';
- if (content) {
- html += '';
- html += `
${escapeHtml(content)}`;
- html += '
';
+ // 处理普通读取模式
+ if (readType === 'read') {
+ const content = result.content || result.text || '';
+ if (content) {
+ html += '';
+ html += `
${escapeHtml(content)}`;
+ html += '
';
+ }
+ }
+ // 处理搜索模式
+ else if (readType === 'search') {
+ const matches = result.matches || [];
+ const query = result.query || args.query || '';
+
+ if (query) {
+ html += '';
+ }
+
+ if (matches.length > 0) {
+ html += '';
+ matches.forEach((match: any, idx: number) => {
+ if (idx > 0) html += '
⋯
';
+ html += '
';
+ html += `
行 ${match.line_number || '?'}
`;
+ html += `
${escapeHtml(match.snippet || match.content || '')}`;
+ html += '
';
+ });
+ html += '
';
+ } else {
+ html += '未找到匹配内容
';
+ }
+ }
+ // 处理提取模式
+ else if (readType === 'extract') {
+ const segments = result.segments || [];
+
+ if (segments.length > 0) {
+ html += '';
+
+ html += '';
+ segments.forEach((segment: any, idx: number) => {
+ if (idx > 0) html += '
⋯
';
+ html += '
';
+ const startLine = segment.start_line || segment.line_start || '?';
+ const endLine = segment.end_line || segment.line_end || '?';
+ html += `
行 ${startLine}-${endLine}
`;
+ html += `
${escapeHtml(segment.content || segment.text || '')}`;
+ html += '
';
+ });
+ html += '
';
+ } else {
+ html += '未提取到内容
';
+ }
}
return html;
@@ -883,4 +986,76 @@ function renderEasterEgg(result: any, args: any): string {
font-size: 14px;
line-height: 1.6;
}
+
+/* 搜索匹配项 */
+.search-match-separator {
+ text-align: center;
+ color: rgba(0, 0, 0, 0.3);
+ margin: 12px 0;
+ font-size: 16px;
+}
+
+.search-match-item {
+ margin-bottom: 12px;
+ padding: 8px;
+ background: rgba(0, 0, 0, 0.02);
+ border-radius: 6px;
+}
+
+:root[data-theme='dark'] .search-match-item {
+ background: rgba(255, 255, 255, 0.05);
+}
+
+.search-match-line {
+ font-size: 11px;
+ color: rgba(0, 0, 0, 0.5);
+ margin-bottom: 4px;
+ font-weight: 600;
+}
+
+:root[data-theme='dark'] .search-match-line {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.search-match-item pre {
+ margin: 0;
+ font-size: 12px;
+ line-height: 1.5;
+}
+
+/* 提取片段 */
+.segment-separator {
+ text-align: center;
+ color: rgba(0, 0, 0, 0.3);
+ margin: 12px 0;
+ font-size: 16px;
+}
+
+.segment-item {
+ margin-bottom: 12px;
+ padding: 8px;
+ background: rgba(0, 0, 0, 0.02);
+ border-radius: 6px;
+}
+
+:root[data-theme='dark'] .segment-item {
+ background: rgba(255, 255, 255, 0.05);
+}
+
+.segment-range {
+ font-size: 11px;
+ color: rgba(0, 0, 0, 0.5);
+ margin-bottom: 4px;
+ font-weight: 600;
+}
+
+:root[data-theme='dark'] .segment-range {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.segment-item pre {
+ margin: 0;
+ font-size: 12px;
+ line-height: 1.5;
+}
diff --git a/static/src/components/chat/actions/toolRenderers.ts b/static/src/components/chat/actions/toolRenderers.ts
index 9edaa18..7b9b62b 100644
--- a/static/src/components/chat/actions/toolRenderers.ts
+++ b/static/src/components/chat/actions/toolRenderers.ts
@@ -189,33 +189,79 @@ function renderCreateFile(result: any, args: any): string {
}
function renderWriteFile(result: any, args: any): string {
- const path = args.path || result.path || '';
+ const path = args.file_path || args.path || result.path || '';
const status = result.success ? '✓ 已写入' : '✗ 失败';
+ const content = args.content || '';
+ const isAppend = args.append || false;
let html = '';
+ // 显示写入的内容
+ if (content) {
+ html += '';
+ }
+
return html;
}
function renderEditFile(result: any, args: any): string {
const path = args.file_path || args.path || result.path || '';
const status = result.success ? '✓ 已编辑' : '✗ 失败';
+
+ // 兼容两种数据格式:
+ // 1. 新格式:args.replacements 数组
+ // 2. 旧格式:args.old_string 和 args.new_string
+ const replacements = args.replacements || [];
let oldString = args.old_string || '';
let newString = args.new_string || '';
- // 处理转义的 \n
- oldString = oldString.replace(/\\n/g, '\n');
- newString = newString.replace(/\\n/g, '\n');
-
let html = '';
- if (oldString || newString) {
+ // 如果有 replacements 数组,使用新格式
+ if (replacements.length > 0) {
+ html += '';
+ }
+ // 否则使用旧格式(old_string/new_string)
+ else if (oldString || newString) {
+ // 处理转义的 \n
+ oldString = oldString.replace(/\\n/g, '\n');
+ newString = newString.replace(/\\n/g, '\n');
+
html += '