fix: 修复工具增强显示的多个问题
1. read_file 工具支持三种模式显示 - read 模式:显示完整文件内容 - search 模式:显示搜索匹配项和行号 - extract 模式:显示提取的多个片段 2. edit_file 工具兼容新旧数据格式 - 支持 old_string/new_string 格式(当前后端使用) - 支持 replacements 数组格式(为未来扩展预留) 3. write_file 工具显示写入内容 - 显示完整的写入内容 - 使用绿色+号样式(与 edit_file 一致) - 支持显示追加模式标识 4. 极简模式工具图标匹配修复 - 修复所有工具图标都显示为 run_command 的问题 - 使用与堆叠块模式相同的图标获取逻辑 - 正确显示每个工具对应的图标 5. 新增样式支持 - 搜索匹配项样式 - 提取片段样式 - 移除彩色侧边框,保持简洁 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6697da9581
commit
a030ff954b
@ -34,9 +34,11 @@
|
|||||||
class="step-item"
|
class="step-item"
|
||||||
>
|
>
|
||||||
<div class="step-timeline">
|
<div class="step-timeline">
|
||||||
<svg class="step-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<span
|
||||||
<g v-html="getStepIcon(step)"></g>
|
class="step-icon icon icon-sm"
|
||||||
</svg>
|
:style="getStepIconStyle(step)"
|
||||||
|
aria-hidden="true"
|
||||||
|
></span>
|
||||||
<div class="step-line"></div>
|
<div class="step-line"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-content">
|
<div class="step-content">
|
||||||
@ -249,8 +251,14 @@ const toggleExpand = (groupId: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStepIcon = (step: Step) => {
|
const getStepIconStyle = (step: Step) => {
|
||||||
return step.type === 'thinking' ? getIconPath('brain') : getIconPath('terminal');
|
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) => {
|
const getToolName = (action: Action) => {
|
||||||
@ -322,15 +330,6 @@ const escapeHtml = (text: string) => {
|
|||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIconPath = (name: string) => {
|
|
||||||
const icons: Record<string, string> = {
|
|
||||||
brain: '<path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"></path><path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"></path><path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"></path><path d="M17.599 6.5a3 3 0 0 0 .399-1.375"></path><path d="M6.003 5.125A3 3 0 0 0 6.401 6.5"></path><path d="M3.477 10.896a4 4 0 0 1 .585-.396"></path><path d="M19.938 10.5a4 4 0 0 1 .585.396"></path><path d="M6 18a4 4 0 0 1-1.967-.516"></path><path d="M19.967 17.484A4 4 0 0 1 18 18"></path>',
|
|
||||||
terminal: '<polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line>',
|
|
||||||
search: '<circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path>'
|
|
||||||
};
|
|
||||||
return icons[name] || icons.brain;
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(() => props.actions, () => {
|
watch(() => props.actions, () => {
|
||||||
// 简化:不需要动态更新高度
|
// 简化:不需要动态更新高度
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
@ -465,7 +464,6 @@ watch(() => props.actions, () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.step-icon {
|
.step-icon {
|
||||||
color: var(--claude-text-secondary);
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -244,27 +244,49 @@ function renderCreateFile(result: any, args: any): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderWriteFile(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 status = result.success ? '✓ 已写入' : '✗ 失败';
|
||||||
|
const content = args.content || '';
|
||||||
|
const isAppend = args.append || false;
|
||||||
|
|
||||||
let html = '<div class="tool-result-meta">';
|
let html = '<div class="tool-result-meta">';
|
||||||
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
||||||
html += `<div><strong>状态:</strong>${status}</div>`;
|
html += `<div><strong>状态:</strong>${status}</div>`;
|
||||||
|
if (isAppend) {
|
||||||
|
html += `<div><strong>模式:</strong>追加</div>`;
|
||||||
|
}
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
|
// 显示写入的内容
|
||||||
|
if (content) {
|
||||||
|
html += '<div class="tool-result-diff scrollable">';
|
||||||
|
const lines = content.split('\n');
|
||||||
|
lines.forEach((line: string) => {
|
||||||
|
html += `<div class="diff-line diff-add">+ ${escapeHtml(line)}</div>`;
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderEditFile(result: any, args: any): string {
|
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 ? '✓ 已编辑' : '✗ 失败';
|
const status = result.success ? '✓ 已编辑' : '✗ 失败';
|
||||||
|
|
||||||
|
// 兼容两种数据格式:
|
||||||
|
// 1. 新格式:args.replacements 数组
|
||||||
|
// 2. 旧格式:args.old_string 和 args.new_string
|
||||||
const replacements = args.replacements || [];
|
const replacements = args.replacements || [];
|
||||||
|
let oldString = args.old_string || '';
|
||||||
|
let newString = args.new_string || '';
|
||||||
|
|
||||||
let html = '<div class="tool-result-meta">';
|
let html = '<div class="tool-result-meta">';
|
||||||
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
||||||
html += `<div><strong>状态:</strong>${status}</div>`;
|
html += `<div><strong>状态:</strong>${status}</div>`;
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
|
// 如果有 replacements 数组,使用新格式
|
||||||
if (replacements.length > 0) {
|
if (replacements.length > 0) {
|
||||||
html += '<div class="tool-result-diff scrollable">';
|
html += '<div class="tool-result-diff scrollable">';
|
||||||
replacements.forEach((rep: any, idx: number) => {
|
replacements.forEach((rep: any, idx: number) => {
|
||||||
@ -289,6 +311,30 @@ function renderEditFile(result: any, args: any): string {
|
|||||||
});
|
});
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
// 否则使用旧格式(old_string/new_string)
|
||||||
|
else if (oldString || newString) {
|
||||||
|
// 处理转义的 \n
|
||||||
|
oldString = oldString.replace(/\\n/g, '\n');
|
||||||
|
newString = newString.replace(/\\n/g, '\n');
|
||||||
|
|
||||||
|
html += '<div class="tool-result-diff scrollable">';
|
||||||
|
|
||||||
|
if (oldString) {
|
||||||
|
const oldLines = oldString.split('\n');
|
||||||
|
oldLines.forEach((line: string) => {
|
||||||
|
html += `<div class="diff-line diff-remove">- ${escapeHtml(line)}</div>`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newString) {
|
||||||
|
const newLines = newString.split('\n');
|
||||||
|
newLines.forEach((line: string) => {
|
||||||
|
html += `<div class="diff-line diff-add">+ ${escapeHtml(line)}</div>`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
@ -324,7 +370,7 @@ function renderReadFile(result: any, args: any): string {
|
|||||||
const path = args.path || result.path || '';
|
const path = args.path || result.path || '';
|
||||||
const status = result.success ? '✓ 成功' : '✗ 失败';
|
const status = result.success ? '✓ 成功' : '✗ 失败';
|
||||||
const size = result.size || result.file_size || 0;
|
const size = result.size || result.file_size || 0;
|
||||||
const content = result.content || result.text || '';
|
const readType = result.type || 'read';
|
||||||
|
|
||||||
let html = '<div class="tool-result-meta">';
|
let html = '<div class="tool-result-meta">';
|
||||||
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
||||||
@ -332,12 +378,69 @@ function renderReadFile(result: any, args: any): string {
|
|||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
html += `<div><strong>大小:</strong>${formatBytes(size)}</div>`;
|
html += `<div><strong>大小:</strong>${formatBytes(size)}</div>`;
|
||||||
}
|
}
|
||||||
|
if (readType !== 'read') {
|
||||||
|
html += `<div><strong>模式:</strong>${escapeHtml(readType)}</div>`;
|
||||||
|
}
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
if (content) {
|
// 处理普通读取模式
|
||||||
html += '<div class="tool-result-content scrollable">';
|
if (readType === 'read') {
|
||||||
html += `<pre>${escapeHtml(content)}</pre>`;
|
const content = result.content || result.text || '';
|
||||||
html += '</div>';
|
if (content) {
|
||||||
|
html += '<div class="tool-result-content scrollable">';
|
||||||
|
html += `<pre>${escapeHtml(content)}</pre>`;
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理搜索模式
|
||||||
|
else if (readType === 'search') {
|
||||||
|
const matches = result.matches || [];
|
||||||
|
const query = result.query || args.query || '';
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
html += '<div class="tool-result-meta">';
|
||||||
|
html += `<div><strong>搜索关键词:</strong>${escapeHtml(query)}</div>`;
|
||||||
|
html += `<div><strong>匹配数量:</strong>${matches.length}</div>`;
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches.length > 0) {
|
||||||
|
html += '<div class="tool-result-content scrollable">';
|
||||||
|
matches.forEach((match: any, idx: number) => {
|
||||||
|
if (idx > 0) html += '<div class="search-match-separator">⋯</div>';
|
||||||
|
html += '<div class="search-match-item">';
|
||||||
|
html += `<div class="search-match-line">行 ${match.line_number || '?'}</div>`;
|
||||||
|
html += `<pre>${escapeHtml(match.snippet || match.content || '')}</pre>`;
|
||||||
|
html += '</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
} else {
|
||||||
|
html += '<div class="tool-result-empty">未找到匹配内容</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理提取模式
|
||||||
|
else if (readType === 'extract') {
|
||||||
|
const segments = result.segments || [];
|
||||||
|
|
||||||
|
if (segments.length > 0) {
|
||||||
|
html += '<div class="tool-result-meta">';
|
||||||
|
html += `<div><strong>提取片段数:</strong>${segments.length}</div>`;
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<div class="tool-result-content scrollable">';
|
||||||
|
segments.forEach((segment: any, idx: number) => {
|
||||||
|
if (idx > 0) html += '<div class="segment-separator">⋯</div>';
|
||||||
|
html += '<div class="segment-item">';
|
||||||
|
const startLine = segment.start_line || segment.line_start || '?';
|
||||||
|
const endLine = segment.end_line || segment.line_end || '?';
|
||||||
|
html += `<div class="segment-range">行 ${startLine}-${endLine}</div>`;
|
||||||
|
html += `<pre>${escapeHtml(segment.content || segment.text || '')}</pre>`;
|
||||||
|
html += '</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
} else {
|
||||||
|
html += '<div class="tool-result-empty">未提取到内容</div>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
@ -883,4 +986,76 @@ function renderEasterEgg(result: any, args: any): string {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.6;
|
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;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -189,33 +189,79 @@ function renderCreateFile(result: any, args: any): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderWriteFile(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 status = result.success ? '✓ 已写入' : '✗ 失败';
|
||||||
|
const content = args.content || '';
|
||||||
|
const isAppend = args.append || false;
|
||||||
|
|
||||||
let html = '<div class="tool-result-meta">';
|
let html = '<div class="tool-result-meta">';
|
||||||
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
||||||
html += `<div><strong>状态:</strong>${status}</div>`;
|
html += `<div><strong>状态:</strong>${status}</div>`;
|
||||||
|
if (isAppend) {
|
||||||
|
html += `<div><strong>模式:</strong>追加</div>`;
|
||||||
|
}
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
|
// 显示写入的内容
|
||||||
|
if (content) {
|
||||||
|
html += '<div class="tool-result-diff scrollable">';
|
||||||
|
const lines = content.split('\n');
|
||||||
|
lines.forEach((line: string) => {
|
||||||
|
html += `<div class="diff-line diff-add">+ ${escapeHtml(line)}</div>`;
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderEditFile(result: any, args: any): string {
|
function renderEditFile(result: any, args: any): string {
|
||||||
const path = args.file_path || args.path || result.path || '';
|
const path = args.file_path || args.path || result.path || '';
|
||||||
const status = result.success ? '✓ 已编辑' : '✗ 失败';
|
const status = result.success ? '✓ 已编辑' : '✗ 失败';
|
||||||
|
|
||||||
|
// 兼容两种数据格式:
|
||||||
|
// 1. 新格式:args.replacements 数组
|
||||||
|
// 2. 旧格式:args.old_string 和 args.new_string
|
||||||
|
const replacements = args.replacements || [];
|
||||||
let oldString = args.old_string || '';
|
let oldString = args.old_string || '';
|
||||||
let newString = args.new_string || '';
|
let newString = args.new_string || '';
|
||||||
|
|
||||||
// 处理转义的 \n
|
|
||||||
oldString = oldString.replace(/\\n/g, '\n');
|
|
||||||
newString = newString.replace(/\\n/g, '\n');
|
|
||||||
|
|
||||||
let html = '<div class="tool-result-meta">';
|
let html = '<div class="tool-result-meta">';
|
||||||
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
||||||
html += `<div><strong>状态:</strong>${status}</div>`;
|
html += `<div><strong>状态:</strong>${status}</div>`;
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
if (oldString || newString) {
|
// 如果有 replacements 数组,使用新格式
|
||||||
|
if (replacements.length > 0) {
|
||||||
|
html += '<div class="tool-result-diff scrollable">';
|
||||||
|
replacements.forEach((rep: any, idx: number) => {
|
||||||
|
if (idx > 0) html += '<div class="diff-separator">⋮</div>';
|
||||||
|
|
||||||
|
const oldText = rep.old_text || rep.old || '';
|
||||||
|
const newText = rep.new_text || rep.new || '';
|
||||||
|
|
||||||
|
if (oldText) {
|
||||||
|
const oldLines = oldText.split('\n');
|
||||||
|
oldLines.forEach((line: string) => {
|
||||||
|
html += `<div class="diff-line diff-remove">- ${escapeHtml(line)}</div>`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newText) {
|
||||||
|
const newLines = newText.split('\n');
|
||||||
|
newLines.forEach((line: string) => {
|
||||||
|
html += `<div class="diff-line diff-add">+ ${escapeHtml(line)}</div>`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
// 否则使用旧格式(old_string/new_string)
|
||||||
|
else if (oldString || newString) {
|
||||||
|
// 处理转义的 \n
|
||||||
|
oldString = oldString.replace(/\\n/g, '\n');
|
||||||
|
newString = newString.replace(/\\n/g, '\n');
|
||||||
|
|
||||||
html += '<div class="tool-result-diff scrollable">';
|
html += '<div class="tool-result-diff scrollable">';
|
||||||
|
|
||||||
if (oldString) {
|
if (oldString) {
|
||||||
@ -269,7 +315,7 @@ function renderReadFile(result: any, args: any): string {
|
|||||||
const path = args.path || result.path || '';
|
const path = args.path || result.path || '';
|
||||||
const status = result.success ? '✓ 成功' : '✗ 失败';
|
const status = result.success ? '✓ 成功' : '✗ 失败';
|
||||||
const size = result.size || result.file_size || 0;
|
const size = result.size || result.file_size || 0;
|
||||||
const content = result.content || result.text || '';
|
const readType = result.type || 'read';
|
||||||
|
|
||||||
let html = '<div class="tool-result-meta">';
|
let html = '<div class="tool-result-meta">';
|
||||||
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
|
||||||
@ -277,12 +323,69 @@ function renderReadFile(result: any, args: any): string {
|
|||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
html += `<div><strong>大小:</strong>${formatBytes(size)}</div>`;
|
html += `<div><strong>大小:</strong>${formatBytes(size)}</div>`;
|
||||||
}
|
}
|
||||||
|
if (readType !== 'read') {
|
||||||
|
html += `<div><strong>模式:</strong>${escapeHtml(readType)}</div>`;
|
||||||
|
}
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
if (content) {
|
// 处理普通读取模式
|
||||||
html += '<div class="tool-result-content scrollable">';
|
if (readType === 'read') {
|
||||||
html += `<pre>${escapeHtml(content)}</pre>`;
|
const content = result.content || result.text || '';
|
||||||
html += '</div>';
|
if (content) {
|
||||||
|
html += '<div class="tool-result-content scrollable">';
|
||||||
|
html += `<pre>${escapeHtml(content)}</pre>`;
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理搜索模式
|
||||||
|
else if (readType === 'search') {
|
||||||
|
const matches = result.matches || [];
|
||||||
|
const query = result.query || args.query || '';
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
html += '<div class="tool-result-meta">';
|
||||||
|
html += `<div><strong>搜索关键词:</strong>${escapeHtml(query)}</div>`;
|
||||||
|
html += `<div><strong>匹配数量:</strong>${matches.length}</div>`;
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches.length > 0) {
|
||||||
|
html += '<div class="tool-result-content scrollable">';
|
||||||
|
matches.forEach((match: any, idx: number) => {
|
||||||
|
if (idx > 0) html += '<div class="search-match-separator">⋯</div>';
|
||||||
|
html += '<div class="search-match-item">';
|
||||||
|
html += `<div class="search-match-line">行 ${match.line_number || '?'}</div>`;
|
||||||
|
html += `<pre>${escapeHtml(match.snippet || match.content || '')}</pre>`;
|
||||||
|
html += '</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
} else {
|
||||||
|
html += '<div class="tool-result-empty">未找到匹配内容</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理提取模式
|
||||||
|
else if (readType === 'extract') {
|
||||||
|
const segments = result.segments || [];
|
||||||
|
|
||||||
|
if (segments.length > 0) {
|
||||||
|
html += '<div class="tool-result-meta">';
|
||||||
|
html += `<div><strong>提取片段数:</strong>${segments.length}</div>`;
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<div class="tool-result-content scrollable">';
|
||||||
|
segments.forEach((segment: any, idx: number) => {
|
||||||
|
if (idx > 0) html += '<div class="segment-separator">⋯</div>';
|
||||||
|
html += '<div class="segment-item">';
|
||||||
|
const startLine = segment.start_line || segment.line_start || '?';
|
||||||
|
const endLine = segment.end_line || segment.line_end || '?';
|
||||||
|
html += `<div class="segment-range">行 ${startLine}-${endLine}</div>`;
|
||||||
|
html += `<pre>${escapeHtml(segment.content || segment.text || '')}</pre>`;
|
||||||
|
html += '</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
} else {
|
||||||
|
html += '<div class="tool-result-empty">未提取到内容</div>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user