claude-desktop/src/lib/utils/toolCards.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

122 lines
4.3 KiB
TypeScript

// Helper fuer Inline-Tool-Karten in Assistant-Messages (Phase 8)
//
// Mapping Tool-Name -> Icon, Akzentfarbe, Default-Collapse-Verhalten,
// Subtitel-Generierung. Die eigentliche Card-Komponente wird in
// ToolCallCard.svelte gewaehlt; hier sind nur statische Metadaten.
export type ToolKind =
| 'read' // Read, Glob, NotebookRead
| 'edit' // Edit, Write, NotebookEdit, MultiEdit
| 'bash' // Bash, BashOutput, KillShell
| 'search' // Grep, Glob (alternativ)
| 'web' // WebFetch, WebSearch
| 'task' // Task (Subagent)
| 'mcp' // mcp__*
| 'todo' // TodoWrite
| 'generic';
export interface ToolMeta {
kind: ToolKind;
icon: string; // Emoji-Icon (matcht VS-Code-Extension-Konvention)
label: string; // Lesbarer Tool-Name (Original beibehalten wenn moeglich)
collapseWhenDone: boolean; // VS-Code: Read collapsed nach Erfolg, Edit aufgeklappt
}
export function getToolMeta(toolName: string): ToolMeta {
const t = (toolName || '').toLowerCase();
// MCP-Tools (mcp__server__name)
if (t.startsWith('mcp__')) {
return { kind: 'mcp', icon: '🛠️', label: toolName, collapseWhenDone: true };
}
// Edit/Write-Familie
if (t === 'edit' || t === 'write' || t === 'multiedit' || t === 'notebookedit') {
return { kind: 'edit', icon: '✏️', label: toolName, collapseWhenDone: false };
}
// Read-Familie
if (t === 'read' || t === 'notebookread') {
return { kind: 'read', icon: '📖', label: toolName, collapseWhenDone: true };
}
// Bash-Familie
if (t === 'bash' || t === 'bashoutput' || t === 'killshell') {
return { kind: 'bash', icon: '🖥️', label: toolName, collapseWhenDone: true };
}
// Such-Tools
if (t === 'grep') return { kind: 'search', icon: '🔍', label: toolName, collapseWhenDone: true };
if (t === 'glob') return { kind: 'search', icon: '📁', label: toolName, collapseWhenDone: true };
// Web-Tools
if (t === 'webfetch') return { kind: 'web', icon: '🌐', label: toolName, collapseWhenDone: true };
if (t === 'websearch') return { kind: 'web', icon: '🔎', label: toolName, collapseWhenDone: true };
// Subagent-Spawn
if (t === 'task') return { kind: 'task', icon: '👥', label: toolName, collapseWhenDone: false };
// Todo
if (t === 'todowrite') return { kind: 'todo', icon: '📋', label: toolName, collapseWhenDone: true };
// Fallback
return { kind: 'generic', icon: '⚙️', label: toolName || 'Tool', collapseWhenDone: true };
}
// Erzeugt einen kompakten Untertitel fuer den Karten-Header.
// Bsp: { file_path: "src/app.ts", offset: 45, limit: 35 } -> "src/app.ts:45-79"
export function getToolSubtitle(tool: string, input: Record<string, unknown> = {}): string {
const t = (tool || '').toLowerCase();
const path = (input.file_path || input.path || input.notebook_path) as string | undefined;
if (path) {
// Pfad kuerzen: "/mnt/17 - Entwicklungen/.../foo/bar.ts" -> "bar.ts"
const short = path.split('/').slice(-2).join('/');
if (t === 'read' || t === 'notebookread') {
const offset = input.offset as number | undefined;
const limit = input.limit as number | undefined;
if (typeof offset === 'number' && typeof limit === 'number') {
return `${short}:${offset}-${offset + limit - 1}`;
}
return short;
}
return short;
}
if (t === 'bash' || t === 'bashoutput') {
const cmd = (input.command as string) || (input.bash_id as string) || '';
return cmd.length > 60 ? cmd.slice(0, 57) + '…' : cmd;
}
if (t === 'grep') {
const pattern = (input.pattern as string) || '';
const where = (input.path as string) || (input.glob as string) || '';
return where ? `"${pattern}" in ${where}` : `"${pattern}"`;
}
if (t === 'glob') {
return (input.pattern as string) || '';
}
if (t === 'webfetch') return (input.url as string) || '';
if (t === 'websearch') return (input.query as string) || '';
if (t === 'task') {
const desc = (input.description as string) || (input.subagent_type as string) || '';
return desc;
}
if (t.startsWith('mcp__')) {
// Tool-Name selbst ist informativ genug
return '';
}
if (t === 'todowrite') {
const todos = input.todos as Array<{ status?: string }> | undefined;
if (Array.isArray(todos)) {
const total = todos.length;
const done = todos.filter(x => x.status === 'completed').length;
return `${done}/${total} erledigt`;
}
return '';
}
return '';
}