claude-desktop/src/routes/+page.svelte
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

142 lines
4.2 KiB
Svelte

<script lang="ts">
// Hauptfenster-Layout (Phase 9, Cursor-Stil 2-spaltig)
//
// ┌──────────────────────────────────────────┐
// │ Sidebar (240px) │ Hauptbereich (flex) │
// │ - Suche │ ChatPanel │
// │ - Sessions │ (oder Detached │
// │ - Nav-Rail │ Placeholder) │
// └──────────────────────────────────────────┘
// ToolDrawer wird ueber Sidebar-Nav getriggert und ueberlagert von rechts.
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { onMount } from 'svelte';
import { chatDetached } from '$lib/stores/app';
import ChatPanel from '$lib/components/ChatPanel.svelte';
import Sidebar, { type DrawerSection } from '$lib/components/Sidebar.svelte';
import ToolDrawer from '$lib/components/ToolDrawer.svelte';
import QuickActions from '$lib/components/QuickActions.svelte';
let activeDrawer = $state<DrawerSection | null>(null);
let showQuickActions = $state(false);
function toggleDrawer(section: DrawerSection) {
activeDrawer = activeDrawer === section ? null : section;
}
function openSearch() {
showQuickActions = true;
}
onMount(async () => {
// Detach/Reattach
await listen('chat-reattached', () => { $chatDetached = false; });
await listen('chat-detached', () => { $chatDetached = true; });
// Globaler Cmd/Ctrl+K Listener fuer Quick-Actions
const handler = (e: KeyboardEvent) => {
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
e.preventDefault();
showQuickActions = !showQuickActions;
}
// Esc schliesst Drawer
if (e.key === 'Escape' && activeDrawer) {
activeDrawer = null;
}
};
window.addEventListener('keydown', handler);
// Quick-Actions Navigation: Drawer-Sektion bei Sub-Tab-Wahl oeffnen
await listen<{ panel: string; tab: string }>('navigate-tab', (event) => {
const t = event.payload.tab;
// Mappe Sub-Tab → Section
if (['activity', 'monitor', 'perf'].includes(t)) activeDrawer = 'activity';
else if (['memory', 'knowledge', 'context'].includes(t)) activeDrawer = 'memory';
else if (['programs', 'voice', 'agents', 'guards', 'hooks'].includes(t)) activeDrawer = 'tools';
else if (['settings', 'audit'].includes(t)) activeDrawer = 'settings';
});
return () => window.removeEventListener('keydown', handler);
});
function handleQuickAction(action: any) {
showQuickActions = false;
// Wenn die Action eine navigate-tab Anweisung war, oeffnet der Listener den Drawer
// Andere Actions werden vom ChatPanel verarbeitet (Slash-Commands etc.)
if (action?.invoke) {
invoke(action.invoke, action.invokeArgs ?? {}).catch((e) =>
console.error('Quick-Action invoke failed:', e)
);
}
}
</script>
<div class="layout">
<Sidebar
{activeDrawer}
onSearchOpen={openSearch}
onDrawerToggle={toggleDrawer}
/>
<div class="main">
{#if !$chatDetached}
<ChatPanel />
{:else}
<div class="detached">
<p>Chat ist in einem eigenen Fenster.</p>
<button class="reattach" onclick={() => invoke('chat_window_close')}>
Zurückholen
</button>
</div>
{/if}
</div>
</div>
<ToolDrawer
section={activeDrawer}
onClose={() => (activeDrawer = null)}
/>
<QuickActions bind:visible={showQuickActions} onExecute={handleQuickAction} />
<style>
.layout {
display: flex;
height: 100%;
min-height: 0;
background: var(--bg-primary);
}
.main {
flex: 1;
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
background: var(--bg-primary);
}
.detached {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--sp-3);
color: var(--text-secondary);
}
.reattach {
padding: var(--sp-2) var(--sp-4);
background: var(--accent);
color: var(--accent-fg);
border: 0;
border-radius: var(--r-sm);
font-size: var(--fs-md);
cursor: pointer;
}
.reattach:hover {
background: var(--accent-hover);
}
</style>