- Tauri 2.0 + SvelteKit Projekt aufgesetzt - Basis-UI mit 3 Panels (Chat, Aktivität, Präsentation) - Roter STOPP-Button Footer - Autonomes Gedächtnis-System (memory.rs) - Änderungs-Log / Audit Trail (audit.rs) - Multi-Agent-View Komponenten - NixOS Entwicklungsumgebung (shell.nix) Phase 1 abgeschlossen, Claude SDK Integration folgt. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
166 lines
3.6 KiB
Svelte
166 lines
3.6 KiB
Svelte
<script lang="ts">
|
|
import { recentToolCalls, agents } from '$lib/stores/app';
|
|
|
|
// Tool-Icons
|
|
const toolIcons: Record<string, string> = {
|
|
Read: '📖',
|
|
Write: '✏️',
|
|
Edit: '✏️',
|
|
Bash: '🖥️',
|
|
Grep: '🔍',
|
|
Glob: '📂',
|
|
Task: '🤖',
|
|
WebFetch: '🌐',
|
|
WebSearch: '🔎'
|
|
};
|
|
|
|
function getToolIcon(tool: string): string {
|
|
return toolIcons[tool] || '🔧';
|
|
}
|
|
|
|
function formatTime(date: Date): string {
|
|
return date.toLocaleTimeString('de-DE', {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit'
|
|
});
|
|
}
|
|
|
|
function getAgentName(agentId: string): string {
|
|
const agent = $agents.find((a) => a.id === agentId);
|
|
if (!agent) return 'Main';
|
|
return agent.type.charAt(0).toUpperCase() + agent.type.slice(1);
|
|
}
|
|
|
|
function formatArgs(args: Record<string, unknown>): string {
|
|
// Versuche sinnvolle Info zu extrahieren
|
|
if (args.file_path) return String(args.file_path).split('/').pop() || '';
|
|
if (args.pattern) return `"${args.pattern}"`;
|
|
if (args.command) {
|
|
const cmd = String(args.command);
|
|
return cmd.length > 30 ? cmd.substring(0, 30) + '...' : cmd;
|
|
}
|
|
if (args.url) return String(args.url);
|
|
return '';
|
|
}
|
|
</script>
|
|
|
|
<div class="activity-panel">
|
|
{#if $recentToolCalls.length === 0}
|
|
<div class="empty-state">
|
|
<p>Noch keine Aktivität.</p>
|
|
<p class="hint">Tool-Aufrufe erscheinen hier in Echtzeit.</p>
|
|
</div>
|
|
{:else}
|
|
<div class="activity-list">
|
|
{#each $recentToolCalls as call}
|
|
<div class="activity-item" class:running={call.status === 'running'} class:failed={call.status === 'failed'}>
|
|
<span class="activity-time">{formatTime(call.startedAt)}</span>
|
|
<span class="activity-icon">{getToolIcon(call.tool)}</span>
|
|
<span class="activity-agent">{getAgentName(call.agentId)}</span>
|
|
<span class="activity-tool">{call.tool}</span>
|
|
<span class="activity-args">{formatArgs(call.args)}</span>
|
|
<span class="activity-status">
|
|
{#if call.status === 'running'}
|
|
<span class="status-dot running"></span>
|
|
{:else if call.status === 'completed'}
|
|
✓
|
|
{:else}
|
|
✗
|
|
{/if}
|
|
</span>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
.activity-panel {
|
|
height: 100%;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.empty-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
color: var(--text-secondary);
|
|
text-align: center;
|
|
padding: var(--spacing-md);
|
|
}
|
|
|
|
.empty-state .hint {
|
|
font-size: 0.75rem;
|
|
margin-top: var(--spacing-sm);
|
|
}
|
|
|
|
.activity-list {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
font-family: var(--font-mono);
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.activity-item {
|
|
display: grid;
|
|
grid-template-columns: 70px 24px 60px 80px 1fr 24px;
|
|
gap: var(--spacing-xs);
|
|
align-items: center;
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
border-bottom: 1px solid var(--bg-secondary);
|
|
transition: background 0.2s ease;
|
|
}
|
|
|
|
.activity-item:hover {
|
|
background: var(--bg-secondary);
|
|
}
|
|
|
|
.activity-item.running {
|
|
background: rgba(74, 222, 128, 0.1);
|
|
}
|
|
|
|
.activity-item.failed {
|
|
background: rgba(239, 68, 68, 0.1);
|
|
}
|
|
|
|
.activity-time {
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.activity-icon {
|
|
text-align: center;
|
|
}
|
|
|
|
.activity-agent {
|
|
color: var(--accent);
|
|
}
|
|
|
|
.activity-tool {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.activity-args {
|
|
color: var(--text-secondary);
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.activity-status {
|
|
text-align: center;
|
|
}
|
|
|
|
.status-dot {
|
|
display: inline-block;
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: var(--success);
|
|
animation: pulse 1s ease-in-out infinite;
|
|
}
|
|
</style>
|