All checks were successful
Build AppImage / build (push) Successful in 8m8s
- Block A: KB-Hint-Pillen im Chat (💡) über Tool-Cards, Klick öffnet KB-Browser - Block B: KB-Usage-Tracking (usage_count/last_used), Sortier-Boost für bewährte Einträge - Block C: Cross-Session-Recall per SQLite-FTS5 (🕒 Pille "Schon mal beantwortet") - Block D: Voice-Konversationsmodus (Langes Halten = Loop mit Barge-In-Unterbrechung) - Block F: Select-Button im Audit-Log (appearance:none + SVG-Chevron, WebKitGTK-Fix) - Block G: Chat-Darstellungseinstellungen (Schriftart, -größe, Zeilenhöhe, Code-Größe) - WorkingIndicator: Deutsche Animationstexte beim Verarbeiten Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
193 lines
5 KiB
Svelte
193 lines
5 KiB
Svelte
<script lang="ts">
|
||
// Inline Tool-Call-Karte fuer Assistant-Messages (Phase 8)
|
||
//
|
||
// Sieht aus wie die Tool-Karten in der Claude-Code-Extension fuer VS Code:
|
||
// [▾ 📖 Read · src/app.ts:45-80]
|
||
// <Inhalt>
|
||
//
|
||
// Inhalt wird per <slot /> von Spezialisierungen gefuellt
|
||
// (ToolCardRead/Edit/Bash/...). Die Basis kuemmert sich um Header,
|
||
// Collapse, Status-Animation, Akzentleiste links.
|
||
|
||
import { getToolMeta, getToolSubtitle } from '$lib/utils/toolCards';
|
||
import type { InlineToolCall } from '$lib/stores';
|
||
|
||
interface Props {
|
||
call: InlineToolCall;
|
||
// Wenn der aufrufende Component bewusst aufgeklappt starten will:
|
||
defaultOpen?: boolean;
|
||
}
|
||
|
||
let { call, defaultOpen }: Props = $props();
|
||
|
||
const meta = $derived(getToolMeta(call.tool));
|
||
const subtitle = $derived(getToolSubtitle(call.tool, call.input));
|
||
|
||
// Manueller Override des Auto-Collapse-Verhaltens
|
||
let userOverride = $state<boolean | null>(null);
|
||
|
||
function autoOpen(): boolean {
|
||
if (defaultOpen !== undefined) return defaultOpen;
|
||
if (call.status === 'running') return true;
|
||
if (call.status === 'error') return true;
|
||
return !meta.collapseWhenDone;
|
||
}
|
||
|
||
const isOpen = $derived(userOverride !== null ? userOverride : autoOpen());
|
||
|
||
function toggle() {
|
||
userOverride = !isOpen;
|
||
}
|
||
|
||
function statusClass(s: string): string {
|
||
if (s === 'running') return 'running';
|
||
if (s === 'error') return 'error';
|
||
return 'success';
|
||
}
|
||
</script>
|
||
|
||
<!-- Tool-Card: kompakte „Background-Aktion"-Pille. Bewusst dezenter als
|
||
Chat-Messages — kleinerer Font, monospaced, gedämpfte Farben.
|
||
Sobald done und nichts Spannendes drin → bleibt collapsed. -->
|
||
<div class="tool-card {statusClass(call.status)}" class:open={isOpen}>
|
||
<button class="card-header" onclick={toggle} aria-expanded={isOpen}>
|
||
<span class="chevron" class:open={isOpen}>›</span>
|
||
<span class="icon">{meta.icon}</span>
|
||
<span class="tool-name">{meta.label}</span>
|
||
{#if subtitle}
|
||
<span class="subtitle">{subtitle}</span>
|
||
{/if}
|
||
<span class="status-spacer"></span>
|
||
{#if call.status === 'running'}
|
||
<span class="status-dots" aria-label="laeuft">
|
||
<span></span><span></span><span></span>
|
||
</span>
|
||
{:else if call.status === 'error'}
|
||
<span class="status-icon error" title="Fehler">✗</span>
|
||
{:else}
|
||
<span class="status-icon ok" title="erledigt">✓</span>
|
||
{/if}
|
||
</button>
|
||
|
||
{#if isOpen}
|
||
<div class="card-body">
|
||
<slot {call} />
|
||
</div>
|
||
{/if}
|
||
</div>
|
||
|
||
<style>
|
||
.tool-card {
|
||
margin: 3px 0;
|
||
font-size: 11.5px;
|
||
overflow: hidden;
|
||
border-radius: 4px;
|
||
border-left: 2px solid var(--vscode-input-border, #3c3c3c);
|
||
opacity: 0.78;
|
||
transition: opacity 0.15s ease;
|
||
}
|
||
.tool-card.running {
|
||
opacity: 1;
|
||
border-left-color: var(--vscode-progressBar-background, #3794ff);
|
||
}
|
||
.tool-card.error {
|
||
opacity: 1;
|
||
border-left-color: var(--vscode-errorForeground, #f48771);
|
||
}
|
||
.tool-card.open {
|
||
opacity: 1;
|
||
}
|
||
.tool-card:hover {
|
||
opacity: 1;
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 100%;
|
||
gap: 6px;
|
||
padding: 3px 8px;
|
||
background: transparent;
|
||
color: var(--vscode-descriptionForeground, #888);
|
||
text-align: left;
|
||
font-size: 11.5px;
|
||
line-height: 1.5;
|
||
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
||
min-height: 22px;
|
||
}
|
||
.card-header:hover {
|
||
background: var(--vscode-list-hoverBackground, rgba(255,255,255,0.04));
|
||
color: var(--vscode-editor-foreground, #ddd);
|
||
}
|
||
|
||
.chevron {
|
||
display: inline-block;
|
||
font-size: 12px;
|
||
width: 8px;
|
||
color: var(--vscode-descriptionForeground);
|
||
transition: transform 0.12s ease;
|
||
flex-shrink: 0;
|
||
}
|
||
.chevron.open {
|
||
transform: rotate(90deg);
|
||
}
|
||
|
||
.icon {
|
||
font-size: 11px;
|
||
line-height: 1;
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.tool-name {
|
||
font-weight: 500;
|
||
color: var(--vscode-foreground, #ccc);
|
||
font-size: 11.5px;
|
||
}
|
||
|
||
.subtitle {
|
||
color: var(--vscode-descriptionForeground);
|
||
font-family: var(--font-mono);
|
||
font-size: 11px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
max-width: 55%;
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.status-spacer { flex: 1; }
|
||
|
||
.status-icon {
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
}
|
||
.status-icon.ok { color: var(--vscode-successForeground, #89d185); opacity: 0.7; }
|
||
.status-icon.error { color: var(--vscode-errorForeground, #f48771); }
|
||
|
||
.status-dots {
|
||
display: inline-flex;
|
||
gap: 3px;
|
||
}
|
||
.status-dots span {
|
||
width: 3px;
|
||
height: 3px;
|
||
background: var(--vscode-progressBar-background, #3794ff);
|
||
border-radius: 50%;
|
||
animation: dotPulse 1.2s ease-in-out infinite;
|
||
}
|
||
.status-dots span:nth-child(2) { animation-delay: 0.15s; }
|
||
.status-dots span:nth-child(3) { animation-delay: 0.3s; }
|
||
@keyframes dotPulse {
|
||
0%, 80%, 100% { opacity: 0.3; transform: scale(0.85); }
|
||
40% { opacity: 1; transform: scale(1); }
|
||
}
|
||
|
||
.card-body {
|
||
padding: 6px 10px 8px 18px;
|
||
font-size: 11.5px;
|
||
font-family: var(--font-mono, monospace);
|
||
color: var(--vscode-descriptionForeground);
|
||
background: var(--vscode-input-background, rgba(0,0,0,0.15));
|
||
border-top: 1px solid var(--vscode-input-border, #3c3c3c);
|
||
}
|
||
</style>
|