shared-gui / GUI-ARCHITECTURE.md
smlflg's picture
Initial public upload from Projekte/shared-gui
74f57c5 verified
|
Raw History Blame Contribute Delete
10.6 kB
# 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) |