diff --git a/desktop/src/components/ChatArea.tsx b/desktop/src/components/ChatArea.tsx index b601a4a..6af5475 100644 --- a/desktop/src/components/ChatArea.tsx +++ b/desktop/src/components/ChatArea.tsx @@ -643,10 +643,14 @@ export function ChatArea({ compact, onOpenDetail }: { compact?: boolean; onOpenD * and Chinese ("让我执行...", "让我尝试..."). These are internal thoughts, not user-facing content. */ function stripToolNarration(content: string): string { - const sentences = content.split(/(?<=[。!?.!?])\s*/); - const filtered = sentences.filter(s => { - const t = s.trim(); - if (!t) return false; + // Process line-by-line to preserve markdown structure (headings, lists, paragraphs) + const lines = content.split('\n'); + const filtered = lines.filter(line => { + const t = line.trim(); + // Keep empty lines (paragraph breaks in markdown) + if (!t) return true; + // Keep markdown structural lines (headings, list items, horizontal rules, blockquotes, code) + if (/^(#{1,6}\s|[-*+]\s|\d+\.\s|>|\s*```|---|\|)/.test(t)) return true; // English narration patterns if (/^(?:Now )?[Ll]et me\s/i.test(t)) return false; if (/^I\s+(?:need to|keep getting|should|will try|have to|can try|must)\s/i.test(t)) return false; @@ -656,8 +660,8 @@ function stripToolNarration(content: string): string { if (/^好的,让我为您/.test(t)) return false; return true; }); - const result = filtered.join(' ').replace(/\s{2,}/g, ' ').trim(); - return result || content; // Fallback: if everything was stripped, show original + const result = filtered.join('\n'); + return result || content; } function MessageBubble({ message, onRetry }: { message: Message; setInput?: (text: string) => void; onRetry?: () => void }) {