From d5471d2ceb347e5f252449538479f28c92230e08 Mon Sep 17 00:00:00 2001 From: xintaofei Date: Sat, 28 Mar 2026 14:30:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B6=88=E6=81=AF=E9=87=8C?= =?UTF-8?q?=E7=9A=84=E8=AF=BB/=E5=86=99=E5=B7=A5=E5=85=B7=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/parsers/claude.rs | 75 +++++++++++++------ src/components/diff/unified-diff-preview.tsx | 2 +- .../message/content-parts-renderer.tsx | 2 +- src/i18n/messages/ar.json | 2 +- src/i18n/messages/de.json | 2 +- src/i18n/messages/es.json | 2 +- src/i18n/messages/fr.json | 2 +- src/i18n/messages/ja.json | 2 +- src/i18n/messages/ko.json | 2 +- src/i18n/messages/pt.json | 2 +- src/i18n/messages/zh-TW.json | 2 +- 11 files changed, 62 insertions(+), 33 deletions(-) diff --git a/src-tauri/src/parsers/claude.rs b/src-tauri/src/parsers/claude.rs index 2061311..fa32fc7 100644 --- a/src-tauri/src/parsers/claude.rs +++ b/src-tauri/src/parsers/claude.rs @@ -633,42 +633,71 @@ impl ClaudeParser { }) .map(|s| s.to_string()); - // For read tools, structurize with start_line from tool_input.offset - let output_preview = - if tool_name == "read" || tool_name == "Read" { - let start_line = value - .get("tool_input") - .and_then(|i| i.get("offset")) - .and_then(|o| o.as_u64()) - .map(|o| o + 1) - .unwrap_or(1); - output_text.map(|text| { - serde_json::json!({ - "start_line": start_line, - "content": text - }) - .to_string() - }) - } else { - output_text - }; + // Don't structurize here — `structurize_read_tool_output` + // will handle Read tool output uniformly after grouping. + let output_preview = output_text; + + // Find the matching ToolUse by tool_name (reverse scan so the + // most recent match wins), then fall back to the last ToolUse + // without a paired ToolResult yet. + let existing_result_ids: std::collections::HashSet = messages + .iter() + .rev() + .find(|m| matches!(m.role, MessageRole::Assistant)) + .map(|m| { + m.content + .iter() + .filter_map(|b| { + if let ContentBlock::ToolResult { + tool_use_id: Some(ref id), + .. + } = 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 .iter() .rev() .find(|m| matches!(m.role, MessageRole::Assistant)) .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| { if let ContentBlock::ToolUse { tool_use_id: Some(ref id), .. } = b { - Some(id.clone()) - } else { - None + if !existing_result_ids.contains(id) { + return Some(id.clone()); + } } + None }) }); diff --git a/src/components/diff/unified-diff-preview.tsx b/src/components/diff/unified-diff-preview.tsx index e294489..9749e74 100644 --- a/src/components/diff/unified-diff-preview.tsx +++ b/src/components/diff/unified-diff-preview.tsx @@ -517,7 +517,7 @@ export function UnifiedDiffPreview({ {files.map((file) => (
diff --git a/src/components/message/content-parts-renderer.tsx b/src/components/message/content-parts-renderer.tsx index 2002ebc..6f38572 100644 --- a/src/components/message/content-parts-renderer.tsx +++ b/src/components/message/content-parts-renderer.tsx @@ -1321,7 +1321,7 @@ function FileToolInput({ }, [isRead, output, content, newSource]) return ( -
+
{isRead ? "READ" : "WRITE"} diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 5536116..5f9298f 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -1485,7 +1485,7 @@ "showingTailOutput": "يتم عرض نهاية المخرجات أثناء البث لتحسين الأداء.", "result": "النتيجة", "unknown": "غير معروف", - "inputTruncated": "Input was truncated — diff may be incomplete.", + "inputTruncated": "تم اقتطاع الإدخال — قد يكون الفرق غير مكتمل.", "replaceAll": "استبدال الكل", "filesCount": "الملفات: {count}", "update": "تحديث", diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 47a3511..c6b179a 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -1485,7 +1485,7 @@ "showingTailOutput": "Zur besseren Performance wird während des Streamings nur die Endausgabe angezeigt.", "result": "Ergebnis", "unknown": "unbekannt", - "inputTruncated": "Input was truncated — diff may be incomplete.", + "inputTruncated": "Eingabe wurde gekürzt — Diff ist möglicherweise unvollständig.", "replaceAll": "ALLES ERSETZEN", "filesCount": "Dateien: {count}", "update": "aktualisieren", diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index 56e75f8..3773b55 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -1485,7 +1485,7 @@ "showingTailOutput": "Mostrando la salida final durante el streaming para mejorar el rendimiento.", "result": "Resultado", "unknown": "desconocido", - "inputTruncated": "Input was truncated — diff may be incomplete.", + "inputTruncated": "La entrada fue truncada — el diff puede estar incompleto.", "replaceAll": "REEMPLAZAR TODO", "filesCount": "Archivos: {count}", "update": "actualizar", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 7cda82d..b9965ef 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -1485,7 +1485,7 @@ "showingTailOutput": "Affichage de la fin de la sortie pendant le streaming pour de meilleures performances.", "result": "Résultat", "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", "filesCount": "Fichiers : {count}", "update": "mettre à jour", diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index 09f00ea..4f7ea5c 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -1485,7 +1485,7 @@ "showingTailOutput": "パフォーマンスのため、ストリーミング中は末尾出力を表示しています。", "result": "結果", "unknown": "不明", - "inputTruncated": "Input was truncated — diff may be incomplete.", + "inputTruncated": "入力が切り詰められました — diff が不完全な可能性があります。", "replaceAll": "すべて置換", "filesCount": "ファイル: {count}", "update": "更新", diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index e338452..f2dc1ac 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -1485,7 +1485,7 @@ "showingTailOutput": "성능을 위해 스트리밍 중에는 출력의 끝부분만 표시합니다.", "result": "결과", "unknown": "알 수 없음", - "inputTruncated": "Input was truncated — diff may be incomplete.", + "inputTruncated": "입력이 잘렸습니다 — diff가 불완전할 수 있습니다.", "replaceAll": "모두 바꾸기", "filesCount": "파일: {count}", "update": "업데이트", diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index f6fe5c7..e99c42f 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -1485,7 +1485,7 @@ "showingTailOutput": "Mostrando a saída final durante o streaming para melhor desempenho.", "result": "Resultado", "unknown": "desconhecido", - "inputTruncated": "Input was truncated — diff may be incomplete.", + "inputTruncated": "A entrada foi truncada — o diff pode estar incompleto.", "replaceAll": "SUBSTITUIR TUDO", "filesCount": "Arquivos: {count}", "update": "atualizar", diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index 270c5d7..8f3c5cc 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -1485,7 +1485,7 @@ "showingTailOutput": "為確保效能,串流輸出時僅顯示尾端內容。", "result": "結果", "unknown": "未知", - "inputTruncated": "Input was truncated — diff may be incomplete.", + "inputTruncated": "輸入已截斷,diff 可能不完整。", "replaceAll": "全部替換", "filesCount": "檔案:{count}", "update": "更新",