优化消息里的读/写工具代码样式

This commit is contained in:
xintaofei
2026-03-28 14:30:12 +08:00
parent 8bd19738d0
commit d5471d2ceb
11 changed files with 62 additions and 33 deletions

View File

@@ -633,42 +633,71 @@ impl ClaudeParser {
}) })
.map(|s| s.to_string()); .map(|s| s.to_string());
// For read tools, structurize with start_line from tool_input.offset // Don't structurize here — `structurize_read_tool_output`
let output_preview = // will handle Read tool output uniformly after grouping.
if tool_name == "read" || tool_name == "Read" { let output_preview = output_text;
let start_line = value
.get("tool_input") // Find the matching ToolUse by tool_name (reverse scan so the
.and_then(|i| i.get("offset")) // most recent match wins), then fall back to the last ToolUse
.and_then(|o| o.as_u64()) // without a paired ToolResult yet.
.map(|o| o + 1) let existing_result_ids: std::collections::HashSet<String> = messages
.unwrap_or(1); .iter()
output_text.map(|text| { .rev()
serde_json::json!({ .find(|m| matches!(m.role, MessageRole::Assistant))
"start_line": start_line, .map(|m| {
"content": text m.content
}) .iter()
.to_string() .filter_map(|b| {
}) if let ContentBlock::ToolResult {
} else { tool_use_id: Some(ref id),
output_text ..
}; } = b
{
Some(id.clone())
} else {
None
}
})
.collect()
})
.unwrap_or_default();
// Find matching ToolUse in the last assistant message and use its ID
let matching_id = messages let matching_id = messages
.iter() .iter()
.rev() .rev()
.find(|m| matches!(m.role, MessageRole::Assistant)) .find(|m| matches!(m.role, MessageRole::Assistant))
.and_then(|m| { .and_then(|m| {
// First: try to find an unpaired ToolUse with the same tool_name
let by_name = m.content.iter().rev().find_map(|b| {
if let ContentBlock::ToolUse {
tool_use_id: Some(ref id),
tool_name: ref tn,
..
} = b
{
if tn == tool_name
&& !existing_result_ids.contains(id)
{
return Some(id.clone());
}
}
None
});
if by_name.is_some() {
return by_name;
}
// Fallback: last unpaired ToolUse regardless of name
m.content.iter().rev().find_map(|b| { m.content.iter().rev().find_map(|b| {
if let ContentBlock::ToolUse { if let ContentBlock::ToolUse {
tool_use_id: Some(ref id), tool_use_id: Some(ref id),
.. ..
} = b } = b
{ {
Some(id.clone()) if !existing_result_ids.contains(id) {
} else { return Some(id.clone());
None }
} }
None
}) })
}); });

View File

@@ -517,7 +517,7 @@ export function UnifiedDiffPreview({
{files.map((file) => ( {files.map((file) => (
<section <section
key={file.key} key={file.key}
className="flex max-h-[480px] flex-col rounded-lg border border-border bg-background" className="flex max-h-[420px] flex-col rounded-lg border border-border bg-background"
> >
<header className="flex shrink-0 items-center gap-2 border-b border-border bg-muted/40 px-3 py-2 text-[11px]"> <header className="flex shrink-0 items-center gap-2 border-b border-border bg-muted/40 px-3 py-2 text-[11px]">
<span className="shrink-0 rounded border border-border bg-background px-1.5 py-0.5 text-[10px] text-muted-foreground"> <span className="shrink-0 rounded border border-border bg-background px-1.5 py-0.5 text-[10px] text-muted-foreground">

View File

@@ -1321,7 +1321,7 @@ function FileToolInput({
}, [isRead, output, content, newSource]) }, [isRead, output, content, newSource])
return ( return (
<section className="flex max-h-[480px] flex-col rounded-lg border border-border bg-background"> <section className="flex max-h-[420px] flex-col rounded-lg border border-border bg-background">
<header className="flex shrink-0 items-center gap-2 border-b border-border bg-muted/40 px-3 py-2 text-[11px]"> <header className="flex shrink-0 items-center gap-2 border-b border-border bg-muted/40 px-3 py-2 text-[11px]">
<span className="shrink-0 rounded border border-border bg-background px-1.5 py-0.5 text-[10px] text-muted-foreground"> <span className="shrink-0 rounded border border-border bg-background px-1.5 py-0.5 text-[10px] text-muted-foreground">
{isRead ? "READ" : "WRITE"} {isRead ? "READ" : "WRITE"}

View File

@@ -1485,7 +1485,7 @@
"showingTailOutput": "يتم عرض نهاية المخرجات أثناء البث لتحسين الأداء.", "showingTailOutput": "يتم عرض نهاية المخرجات أثناء البث لتحسين الأداء.",
"result": "النتيجة", "result": "النتيجة",
"unknown": "غير معروف", "unknown": "غير معروف",
"inputTruncated": "Input was truncated — diff may be incomplete.", "inputTruncated": "تم اقتطاع الإدخال — قد يكون الفرق غير مكتمل.",
"replaceAll": "استبدال الكل", "replaceAll": "استبدال الكل",
"filesCount": "الملفات: {count}", "filesCount": "الملفات: {count}",
"update": "تحديث", "update": "تحديث",

View File

@@ -1485,7 +1485,7 @@
"showingTailOutput": "Zur besseren Performance wird während des Streamings nur die Endausgabe angezeigt.", "showingTailOutput": "Zur besseren Performance wird während des Streamings nur die Endausgabe angezeigt.",
"result": "Ergebnis", "result": "Ergebnis",
"unknown": "unbekannt", "unknown": "unbekannt",
"inputTruncated": "Input was truncateddiff may be incomplete.", "inputTruncated": "Eingabe wurde gekürztDiff ist möglicherweise unvollständig.",
"replaceAll": "ALLES ERSETZEN", "replaceAll": "ALLES ERSETZEN",
"filesCount": "Dateien: {count}", "filesCount": "Dateien: {count}",
"update": "aktualisieren", "update": "aktualisieren",

View File

@@ -1485,7 +1485,7 @@
"showingTailOutput": "Mostrando la salida final durante el streaming para mejorar el rendimiento.", "showingTailOutput": "Mostrando la salida final durante el streaming para mejorar el rendimiento.",
"result": "Resultado", "result": "Resultado",
"unknown": "desconocido", "unknown": "desconocido",
"inputTruncated": "Input was truncated — diff may be incomplete.", "inputTruncated": "La entrada fue truncada — el diff puede estar incompleto.",
"replaceAll": "REEMPLAZAR TODO", "replaceAll": "REEMPLAZAR TODO",
"filesCount": "Archivos: {count}", "filesCount": "Archivos: {count}",
"update": "actualizar", "update": "actualizar",

View File

@@ -1485,7 +1485,7 @@
"showingTailOutput": "Affichage de la fin de la sortie pendant le streaming pour de meilleures performances.", "showingTailOutput": "Affichage de la fin de la sortie pendant le streaming pour de meilleures performances.",
"result": "Résultat", "result": "Résultat",
"unknown": "inconnu", "unknown": "inconnu",
"inputTruncated": "Input was truncated — diff may be incomplete.", "inputTruncated": "L'entrée a été tronquée — le diff peut être incomplet.",
"replaceAll": "TOUT REMPLACER", "replaceAll": "TOUT REMPLACER",
"filesCount": "Fichiers : {count}", "filesCount": "Fichiers : {count}",
"update": "mettre à jour", "update": "mettre à jour",

View File

@@ -1485,7 +1485,7 @@
"showingTailOutput": "パフォーマンスのため、ストリーミング中は末尾出力を表示しています。", "showingTailOutput": "パフォーマンスのため、ストリーミング中は末尾出力を表示しています。",
"result": "結果", "result": "結果",
"unknown": "不明", "unknown": "不明",
"inputTruncated": "Input was truncated — diff may be incomplete.", "inputTruncated": "入力が切り詰められました — diff が不完全な可能性があります。",
"replaceAll": "すべて置換", "replaceAll": "すべて置換",
"filesCount": "ファイル: {count}", "filesCount": "ファイル: {count}",
"update": "更新", "update": "更新",

View File

@@ -1485,7 +1485,7 @@
"showingTailOutput": "성능을 위해 스트리밍 중에는 출력의 끝부분만 표시합니다.", "showingTailOutput": "성능을 위해 스트리밍 중에는 출력의 끝부분만 표시합니다.",
"result": "결과", "result": "결과",
"unknown": "알 수 없음", "unknown": "알 수 없음",
"inputTruncated": "Input was truncated — diff may be incomplete.", "inputTruncated": "입력이 잘렸습니다 — diff가 불완전할 수 있습니다.",
"replaceAll": "모두 바꾸기", "replaceAll": "모두 바꾸기",
"filesCount": "파일: {count}", "filesCount": "파일: {count}",
"update": "업데이트", "update": "업데이트",

View File

@@ -1485,7 +1485,7 @@
"showingTailOutput": "Mostrando a saída final durante o streaming para melhor desempenho.", "showingTailOutput": "Mostrando a saída final durante o streaming para melhor desempenho.",
"result": "Resultado", "result": "Resultado",
"unknown": "desconhecido", "unknown": "desconhecido",
"inputTruncated": "Input was truncated — diff may be incomplete.", "inputTruncated": "A entrada foi truncada — o diff pode estar incompleto.",
"replaceAll": "SUBSTITUIR TUDO", "replaceAll": "SUBSTITUIR TUDO",
"filesCount": "Arquivos: {count}", "filesCount": "Arquivos: {count}",
"update": "atualizar", "update": "atualizar",

View File

@@ -1485,7 +1485,7 @@
"showingTailOutput": "為確保效能,串流輸出時僅顯示尾端內容。", "showingTailOutput": "為確保效能,串流輸出時僅顯示尾端內容。",
"result": "結果", "result": "結果",
"unknown": "未知", "unknown": "未知",
"inputTruncated": "Input was truncated — diff may be incomplete.", "inputTruncated": "輸入已截斷diff 可能不完整。",
"replaceAll": "全部替換", "replaceAll": "全部替換",
"filesCount": "檔案:{count}", "filesCount": "檔案:{count}",
"update": "更新", "update": "更新",