claude-desktop/src/lib/utils/markdown.ts
Eddy ad9833fcb8
Some checks failed
Build AppImage / build (push) Has been cancelled
feat: Phase 8+9 — Inline Tool-Karten + komplettes UI-Redesign auf Cursor/Zed-Niveau [appimage]
Phase 8 (VS-Code-Look Chatbereich):
- Linksbuendige Messages mit Avatar-Spalte, Hover-Actions
- Inline Tool-Karten (Read/Edit/Bash/Generic) in Assistant-Messages
- Edit-Karten zeigen Diff direkt mit Accept/Reject
- Tool-Calls werden via events.ts an letzte Assistant-Message gebunden
- Smart-Sticky-Scroll (Auto-Scroll stoppt wenn User selbst scrollt)
- OOM-Bug durch MutationObserver mit subtree:true behoben

Phase 9 (Komplettes UI-Redesign):
- Design-System in app.css: 4 Graustufen, 1 Akzent (#007acc), 4 Status-Farben,
  5 Schriftgroessen (11/12/13/14/16), 4-Punkt-Spacing, 2 Radius-Werte
- vscode.css als Aliase auf das neue System
- UI-Library src/lib/ui/: Button, Card, Icon, Badge, StatusDot, Tooltip, Drawer, Tabs
- Lucide-svelte fuer SVG-Icons (ersetzt Emojis im Chrome)
- StatusBar (22px) ersetzt ueberfuellten Footer mit 6+ Stats
- Titlebar entruempelt: ✱-Logo + Stop + Schulungsmodus + Version
- 2-spaltiges Layout (Sidebar 240px + Hauptbereich) statt 4-Pane-Zerstueckelung
- ToolDrawer: 13 Panels in 4 Gruppen (Aktivitaet/Speicher/Werkzeuge/Einstellungen),
  jede Gruppe mit internen Tabs, Esc schliesst
- Cmd+K global oeffnet QuickActions als zentrale Navigation
- StatusDot-Komponente ersetzt Emoji-Status (🟢🟡🔴) in AgentView
- Hardgecodete Farben (#ef4444, #22c55e, #eab308 ...) in 9 Komponenten durch
  CSS-Variablen ersetzt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 14:27:09 +02:00

56 lines
2.3 KiB
TypeScript

// Markdown-Renderer fuer Chat-Messages.
// Kompakter Wrapper um marked + Custom Code-Block-Renderer.
// Wird aus Message.svelte (Phase 8) und ChatPanel (legacy) verwendet.
import { marked, type Tokens } from 'marked';
const renderer = new marked.Renderer();
renderer.code = function ({ text, lang }: Tokens.Code): string {
const language = lang || '';
const escapedCode = text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
return `<div class="code-block-wrapper" data-lang="${language}"><pre><code class="language-${language}">${escapedCode}</code></pre></div>`;
};
marked.setOptions({ breaks: true, gfm: true, renderer });
/**
* Erkennt "Denk-Bloecke" im Text und zeigt sie als kompakte Inline-Elemente.
* Zwei Quellen:
* 1. SDK Extended-Thinking (kommt als <div class="thinking-inline"> aus der Bridge)
* 2. Text-basierte Patterns (Lass mich analysieren..., Ich schaue mir...)
*/
function collapseThinkingBlocks(text: string): string {
if (text.includes('<div class="thinking-inline">')) return text;
if (text.includes('<details class="thinking-block">')) {
return text.replace(
/<details class="thinking-block">.*?<div class="thinking-content">([\s\S]*?)<\/div><\/details>/g,
(_m, content) => `<div class="thinking-inline"><span class="thinking-label">\u{1F4AD}</span><span class="thinking-text">${content}</span></div>`
);
}
const thinkingPatterns = [
/^((?:(?: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) {
const match = text.match(pattern);
if (match && match[1] && match[1].split('\n').length > 5) {
const thinkingPart = match[1].trim().replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
const answerPart = match[2].trim();
return `<div class="thinking-inline"><span class="thinking-label">\u{1F4AD}</span><span class="thinking-text">${thinkingPart}</span></div>\n\n${answerPart}`;
}
}
return text;
}
export function renderMarkdown(text: string): string {
try {
return marked.parse(collapseThinkingBlocks(text)) as string;
} catch {
return text;
}
}