# GUI-Architektur — 3 Standalone GTK3 Apps Stand: 2026-03-04 ## 1. Shared Base Module (`~/Projekte/shared-gui/gui_base.py`) Gemeinsame Grundlage fuer alle drei GUIs. Importiert `theme.py` aus ClaudeCodePanel. ### BaseApp Klasse ```python class BaseApp(Gtk.Window): def __init__(self, title, width, height, icon_name=None) ``` Bereitgestellte Features: - **Window Setup**: Titel, Groesse, Icon, `destroy`-Signal - **HeaderBar**: Gtk.HeaderBar mit Titel + optionalem Subtitle - **StatusBar**: Unten, CSS-Klasse `.status-bar`, mit `set_status(text, css_class)` Methode - **Theme**: Automatisches Laden von Catppuccin CSS via `theme.py`, COSMIC dark/light Watcher - **CSS Provider**: Gemeinsam fuer alle Widgets, erweiterbar via `add_css(css_string)` - **Refresh Timer**: `start_refresh(interval_seconds, callback)` — GLib.timeout_add_seconds - **Main Box**: Vertikaler Gtk.Box (`self.content_box`) zwischen Header und StatusBar ### Theme-Modul Wiederverwendung `gui_base.py` importiert direkt aus ClaudeCodePanel: ```python sys.path.insert(0, str(Path.home() / "Projekte/ClaudeCodePanel")) from theme import build_css, setup_theme_watcher, get_palette, is_dark_mode ``` ### Gemeinsame CSS-Klassen (aus theme.py) Alle Apps erben automatisch: - `.panel-header`, `.stat-value`, `.stat-label` — Ueberschriften + Statistiken - `.section-frame`, `.section-title` — Karten/Sektionen - `.status-bar`, `.status-saved`, `.status-error` — Statusleiste - `.shortcut-btn` — Aktionsbuttons - `.monitor-value`, `.cost-value` — Monospace-Werte Zusaetzlich definiert gui_base.py: - `.base-card` — Standardkarte (border-radius 8px, card-bg, border) - `.base-toolbar` — Toolbar-Leiste (mantle-bg, border-radius 8px) - `.base-badge` — Kleine Status-Badges (border-radius 12px) --- ## 2. Design-Specs pro GUI ### 2.1 Sidecar-GUI (`~/Projekte/Sidecar/gui/`) **Zweck:** Visuelles Dashboard fuer den Sidecar Daemon — Rules, Sessions, Findings. **Layout:** ``` +-----------------------------------------------+ | [HeaderBar] Sidecar Control Panel [Reload] | +-----------------------------------------------+ | [Notebook] | | +-------------------------------------------+ | | | Tab: Status | Tab: Rules | Tab: Findings | | | +-------------------------------------------+ | | | | STATUS TAB: | | Daemon: running PID: 12345 Uptime: 2h 15m | | Rules: 22 Findings: 5 Plugins: 3 | | | | RULES TAB: | | [SearchEntry filter] | | +------------------------------------------+ | | | Name | Group | Enabled | Fires | | | | cli_first | enforce | [x] | 12 | | | | gate_rm_rf | gate | [x] | 3 | | | +------------------------------------------+ | | [Enable] [Disable] [Snooze 30m] | | | | FINDINGS TAB: | | [ScrolledWindow mit Finding-Cards] | | Time | Rule | Severity | Message | +-----------------------------------------------+ | [StatusBar] Connected via /tmp/claude-sidecar.sock | +-----------------------------------------------+ ``` **Widgets:** - `Gtk.Notebook` mit 3 Tabs - Status-Tab: Grid mit Labels (key-value), aktualisiert via Refresh - Rules-Tab: `Gtk.ListBox` mit Rows, SearchEntry fuer Filter, Toggle-Buttons - Findings-Tab: `Gtk.ListBox` mit farbcodierten Karten (Severity → Farbe) **Datenquellen:** - Primaer: Unix Socket `/tmp/claude-sidecar.sock` (JSON-Protokoll, wie sidecar-ctl) - Kommandos: `status`, `rules`, `findings`, `enable`, `disable`, `snooze`, `reload` - Socket-Kommunikation non-blocking via `GLib.io_add_watch` auf Socket-FD **Refresh:** 5 Sekunden (Status + Rules), Findings on-demand **Interaktion:** - Rules: Klick auf Row → Enable/Disable Toggle, Rechtsklick → Snooze - Findings: Nur Anzeige, scrollbar - Reload-Button in HeaderBar --- ### 2.2 Tools-GUI (`~/Projekte/ClaudeCodePanel/tools-gui/`) **Zweck:** Session-History + Kosten-Tracking fuer Claude Code. **Layout:** ``` +-----------------------------------------------+ | [HeaderBar] Claude Code Tools [Ref] | +-----------------------------------------------+ | [Notebook] | | +-------------------------------------------+ | | | Tab: Sessions | Tab: Costs | Tab: Tools | | | +-------------------------------------------+ | | | | SESSIONS TAB: | | [SearchEntry] [ComboBox: Sortierung] | | +------------------------------------------+ | | | Projekt | Letztes Msg | Kosten | Res | | | | Sidecar | vor 2h | $0.12 | [>] | | | | ClaudePanel | vor 1d | $0.45 | [>] | | | +------------------------------------------+ | | | | COSTS TAB: | | Heute: $1.23 Woche: $8.50 Monat: $32.10 | | [BarChart via DrawingArea — letzte 7 Tage] | | Top Tools: Read(45) Edit(23) Bash(18) | | | | TOOLS TAB: | | Tool-Usage Breakdown (ListBox) | | Skill-Usage + Missed Skills | +-----------------------------------------------+ | [StatusBar] 12 Sessions | $1.23 heute | +-----------------------------------------------+ ``` **Widgets:** - Sessions-Tab: `Gtk.ListBox` mit Session-Rows, SearchEntry, ComboBox fuer Sortierung - Costs-Tab: Stat-Labels oben, `Gtk.DrawingArea` fuer 7-Tage-Balkendiagramm (Cairo), Tool-Rankings - Tools-Tab: `Gtk.ListBox` mit Tool-Usage, Skill-Usage **Datenquellen:** - `monitor.py` Funktionen: `get_daily_cost()`, `get_top_tools()`, `get_recent_sessions()`, `get_usage_timeline()`, `get_provider_costs()`, `get_skill_usage()`, `get_missed_skills_summary()` - Import: `sys.path.insert(0, "~/Projekte/ClaudeCodePanel")` + `from monitor import ...` **Refresh:** 30 Sekunden (passend zum TTL-Cache in monitor.py) --- ### 2.3 Florian-GUI (`~/Projekte/MyAIGame/florian-gui/`) **Zweck:** TTS Control Panel — Voice-Auswahl, Test, Speed/Pitch, Audio-Playback. **Layout:** ``` +-----------------------------------------------+ | [HeaderBar] Florian TTS Control [Play] | +-----------------------------------------------+ | | | Voice: [ComboBox: florian|katja|conrad|...] | | | | Speed: [=====|========] 1.0x | | Pitch: [=====|========] +0Hz | | | | Format: ( ) mp3 ( ) wav ( ) opus ( ) flac | | | | Text: | | +------------------------------------------+ | | | Guten Tag, ich bin Florian und teste | | | | die Sprachausgabe. | | | +------------------------------------------+ | | | | [ Speak ] [Stop] | | | | +------------------------------------------+ | | | Audio-Wellenform / Fortschrittsbalken | | | +------------------------------------------+ | | | | Server: localhost:5050 [Health: OK] | +-----------------------------------------------+ | [StatusBar] Ready — Florian (de-DE) | +-----------------------------------------------+ ``` **Widgets:** - `Gtk.ComboBoxText` fuer Voice-Auswahl (aus `/v1/audio/voices` Endpoint) - `Gtk.Scale` fuer Speed (0.25 — 4.0, Step 0.25) und Pitch (-20Hz — +20Hz) - `Gtk.RadioButton` Gruppe fuer Format - `Gtk.TextView` fuer Text-Eingabe (mehrzeilig) - `Gtk.Button` Speak + Stop - `Gtk.ProgressBar` fuer Audio-Playback - Health-Indikator (Label mit Farbcode) **Datenquellen:** - HTTP API: `http://localhost:5050` - `GET /v1/audio/voices` → Voice-Liste - `POST /v1/audio/speech` → Audio-Bytes (body: `{input, voice, speed, response_format}`) - `GET /health` → Server-Status - HTTP via `Gio.InputStream` / subprocess `curl` + GLib.spawn_async (kein threading!) **Audio-Playback:** - Tempfile schreiben → `subprocess.Popen(["mpv", "--no-video", tmpfile])` via `GLib.spawn_async` - Stop = Kill des mpv-Prozesses **Refresh:** Health-Check alle 10 Sekunden --- ## 3. Dateistruktur pro Projekt ### shared-gui/ ``` ~/Projekte/shared-gui/ gui_base.py # BaseApp + Theme-Integration + gemeinsame CSS ``` ### Sidecar-GUI ``` ~/Projekte/Sidecar/gui/ main.py # Entry-Point, SidecarApp(BaseApp) socket_client.py # Non-blocking Unix Socket Client (GLib.io_add_watch) tabs/ status_tab.py # Daemon-Status Anzeige rules_tab.py # Rules ListBox + Filter + Enable/Disable findings_tab.py # Findings ListBox ``` ### Tools-GUI ``` ~/Projekte/ClaudeCodePanel/tools-gui/ main.py # Entry-Point, ToolsApp(BaseApp) tabs/ sessions_tab.py # Session-ListBox + Search + Sort costs_tab.py # Kosten-Stats + Cairo BarChart tools_tab.py # Tool-Usage + Skill-Usage ``` ### Florian-GUI ``` ~/Projekte/MyAIGame/florian-gui/ main.py # Entry-Point, FlorianApp(BaseApp) tts_client.py # HTTP Client (Gio/subprocess, non-blocking) audio_player.py # mpv-basierter Player (GLib.spawn_async) ``` --- ## 4. Constraints | Constraint | Detail | |-----------|--------| | **Toolkit** | GTK 3.0 via PyGObject (`gi.require_version("Gtk", "3.0")`) | | **Theme** | Catppuccin Mocha (dark) / Latte (light) via `theme.py` | | **Desktop** | COSMIC — `is_dark_mode()` liest `~/.config/cosmic/CosmicTheme.Mode/v1/is_dark` | | **Event Loop** | GLib Main Loop — KEIN threading, KEIN asyncio im GUI-Thread | | **Timer** | `GLib.timeout_add_seconds()` fuer Refresh, Return `True` fuer Repeat | | **Non-Blocking I/O** | Socket: `GLib.io_add_watch`, HTTP: `GLib.spawn_async` mit Callback | | **CSS** | Alle Styles via `Gtk.CssProvider`, keine inline-Styles | | **GLib.idle_add** | Nur als One-Shot (`return False`), NIE mit `return True` | | **Python** | 3.11+ (match/case erlaubt, aber nicht erforderlich) | | **Abhaengigkeiten** | PyGObject >= 3.42, gir1.2-gtk-3.0 (keine zusaetzlichen Pakete) |