diff --git a/scripts/claude-bridge.js b/scripts/claude-bridge.js index da7b2b4..dd2a006 100644 --- a/scripts/claude-bridge.js +++ b/scripts/claude-bridge.js @@ -543,9 +543,10 @@ async function sendMessage(message, requestId, model = null, contextOverride = n fullText += block.text; sendEvent('text', { text: block.text }); } else if (block.type === 'thinking' && block.thinking) { - // Extended Thinking — als kollabierbaren Block senden + // Extended Thinking — als kollabierbaren Block im ChatGPT/Claude.ai-Style const thinkLines = block.thinking.split('\n').length; - const collapsed = `
💭 Überlegung (${thinkLines} Zeilen)\n\n${block.thinking}\n\n
\n\n`; + const escaped = block.thinking.replace(//g, '>'); + const collapsed = `
Überlegung${thinkLines} Zeilen
${escaped}
\n\n`; fullText += collapsed; sendEvent('text', { text: collapsed }); } else if (block.type === 'tool_use') { diff --git a/src/lib/components/ChatPanel.svelte b/src/lib/components/ChatPanel.svelte index 45f694e..b950897 100644 --- a/src/lib/components/ChatPanel.svelte +++ b/src/lib/components/ChatPanel.svelte @@ -51,14 +51,18 @@ } /** - * Erkennt "Denk-Blöcke" im Text und packt sie in Markdown-
. - * Patterns: Absätze die mit typischen Überlegungs-Phrasen starten, - * gefolgt von der eigentlichen Antwort. + * Erkennt "Denk-Blöcke" im Text und packt sie in kollabierbare Blöcke. + * Zwei Quellen: + * 1. SDK Extended-Thinking (kommt als
von Bridge) + * 2. Text-basierte Patterns (Lass mich analysieren..., Ich schaue mir...) */ function collapseThinkingBlocks(text: string): string { + // Bereits von Bridge als
geliefert? Nicht nochmal wrappen. + if (text.includes('
')) return text; + // Pattern: Text beginnt mit Analyse/Überlegungs-Block, dann kommt die Antwort const thinkingPatterns = [ - /^((?:(?:Lass mich|Ich (?:schaue|prüfe|analysiere|untersuche|überleg)|OK,? (?:lass|ich)|Gut,? (?:lass|ich)|Hmm|Also|Zunächst|Zuerst).*?\n(?:.*\n)*?))((?:\n(?:#{1,3} |(?:\*\*|Die |Das |Hier |Zusammen|Fertig|Erledigt|✅)).*[\s\S]*))/m, + /^((?:(?:Lass mich|Let me|Ich (?:schaue|prüfe|analysiere|untersuche|überleg|werde)|OK,? (?:lass|ich)|Gut,? (?:lass|ich)|Hmm|Also,? |Zunächst|Zuerst|Schauen wir|Jetzt (?:schaue|prüfe|check)).*?\n(?:.*\n)*?))((?:\n(?:#{1,3} |(?:\*\*|Die |Das |Hier |Zusammen|Fertig|Erledigt|Done|✅|---)).*[\s\S]*))/m, ]; for (const pattern of thinkingPatterns) { @@ -67,7 +71,7 @@ const thinkingPart = match[1].trim(); const answerPart = match[2].trim(); const lines = thinkingPart.split('\n').length; - return `
\n💭 Überlegung (${lines} Zeilen)\n\n${thinkingPart}\n\n
\n\n${answerPart}`; + return `
Überlegung${lines} Zeilen
${thinkingPart}
\n\n${answerPart}`; } } @@ -1350,34 +1354,99 @@ background: var(--bg-hover, #333); } - /* Thinking-Block (details/summary) */ + /* Thinking-Block — ChatGPT/Claude.ai Style */ .message-content :global(details.thinking-block) { - margin: 0.4rem 0; - border: 1px solid var(--border, #3a3a3a); - border-radius: 4px; - background: var(--bg-tertiary, #1e1e1e); + margin: 8px 0 12px 0; + background: #161b27; + border: 1px solid #1e2a3a; + border-left: 3px solid #374151; + border-radius: 8px; + overflow: hidden; + transition: border-left-color 0.2s ease; } - .message-content :global(details.thinking-block summary) { - padding: 0.4rem 0.6rem; - cursor: pointer; - font-size: 0.8rem; - color: var(--text-secondary, #888); - user-select: none; - } - - .message-content :global(details.thinking-block summary:hover) { - color: var(--text-primary, #ccc); + .message-content :global(details.thinking-block:hover) { + border-left-color: #6366f1; } .message-content :global(details.thinking-block[open]) { - padding-bottom: 0.4rem; + border-left-color: #6366f1; } - .message-content :global(details.thinking-block[open] > :not(summary)) { - padding: 0 0.6rem; - font-size: 0.8rem; - opacity: 0.8; + .message-content :global(details.thinking-block summary) { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 14px; + cursor: pointer; + font-size: 12px; + color: #64748b; + user-select: none; + list-style: none; + transition: color 0.15s; + } + + .message-content :global(details.thinking-block summary::-webkit-details-marker) { + display: none; + } + + .message-content :global(details.thinking-block summary::marker) { + display: none; + content: ''; + } + + .message-content :global(details.thinking-block summary:hover) { + color: #94a3b8; + } + + .message-content :global(svg.thinking-icon) { + width: 14px; + height: 14px; + color: #6366f1; + flex-shrink: 0; + } + + .message-content :global(.thinking-label) { + font-weight: 500; + letter-spacing: 0.02em; + } + + .message-content :global(.thinking-meta) { + color: #475569; + font-size: 11px; + margin-left: auto; + } + + .message-content :global(svg.thinking-chevron) { + width: 12px; + height: 12px; + transition: transform 0.2s ease; + flex-shrink: 0; + } + + .message-content :global(details.thinking-block[open] svg.thinking-chevron) { + transform: rotate(180deg); + } + + .message-content :global(.thinking-content) { + padding: 0 14px 12px 14px; + font-size: 12px; + line-height: 1.6; + color: #475569; + font-family: 'JetBrains Mono', 'Fira Code', monospace; + white-space: pre-wrap; + max-height: 300px; + overflow-y: auto; + border-top: 1px solid #1e2a3a; + } + + .message-content :global(.thinking-content::-webkit-scrollbar) { + width: 4px; + } + + .message-content :global(.thinking-content::-webkit-scrollbar-thumb) { + background: #374151; + border-radius: 2px; } /* Markdown-Styles innerhalb von Nachrichten */