docs: ARCHITECTURE.md + ARCHITECTURE_QUICK.md aktualisiert

Neue Kapitel in ARCHITECTURE.md:
- Admin-Benutzerverwaltung (CRUD, Impersonate)
- Avatar-System (Editor-Modal, Scale, AI-Generate)
- LaTeX/Math-Rendering (KaTeX-Pipeline)

Erweiterte Kapitel:
- Projektstruktur: WorkspaceHelper, AdminEndpoint, avatar-editor.js, admin-page.js, KaTeX-Dateien
- Tech-Stack: KaTeX 0.16.21
- Datenmodell: default_path, avatar_scale, telegram_allowed_chat_ids
- Endpoints: Admin (16 Gruppen), Workspace (mkdir/rmdir)
- Chat-Pipeline: CancellationToken-Propagierung (Stop-Button)
- Telegram: Chat-ID-Whitelist + Zugriffskontrolle
- Auth: DefaultPath als Base-Path, Impersonate
- Workspace: WorkspaceHelper, relativer Pfad-Auflösung
- Frontend: avatar-editor.js, admin-page, KaTeX, Timestamps

ARCHITECTURE_QUICK.md:
- Admin-Endpoint, WorkspaceHelper, KaTeX, DefaultPath, Impersonate, CancellationToken
This commit is contained in:
Meleki committed 2026-03-06 13:12:42 +01:00
1 parent 011b26c37f
commit a82540e7db
2 files changed
+211 -40

No files matched your search

+197 -34
View File
@@ -28,12 +28,15 @@
14. [Cronjob-System](#cronjob-system)
15. [Krea-Bildgenerierung](#krea-bildgenerierung)
16. [Auth & Multi-User](#auth--multi-user)
17. [Nachrichten teilen (Shared Messages)](#nachrichten-teilen-shared-messages)
18. [Workspace & File-Explorer](#workspace--file-explorer)
19. [Windows Desktop App](#windows-desktop-app)
20. [Deployment](#deployment)
21. [Konfiguration](#konfiguration)
22. [Datenbank-Migrationen](#datenbank-migrationen)
17. [Admin-Benutzerverwaltung](#admin-benutzerverwaltung)
18. [Nachrichten teilen (Shared Messages)](#nachrichten-teilen-shared-messages)
19. [Workspace & File-Explorer](#workspace--file-explorer)
20. [Avatar-System](#avatar-system)
21. [LaTeX/Math-Rendering](#latexmath-rendering)
22. [Windows Desktop App](#windows-desktop-app)
23. [Deployment](#deployment)
24. [Konfiguration](#konfiguration)
25. [Datenbank-Migrationen](#datenbank-migrationen)
---
@@ -46,10 +49,10 @@
├─────────────────────────────────────────────────────────────┤
│ ASP.NET 10 Minimal API │
│ http://0.0.0.0:19000 │
├──────┬──────┬──────┬──────┬──────┬──────┬──────┬───────────┤
│ Auth │ Chat │ Hist │ Sess │Agent │ Tele │ Cron │ Krea │
│ │ SSE │ │ │ │ gram │ jobs │ Images │
├──────┴──────┴──────┴──────┴──────┴──────┴──────┴───────────┤
├──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬────┤
│ Auth │ Chat │ Hist │ Sess │Agent │ Tele │ Cron │ Krea │Adm │
│ │ SSE │ │ │ │ gram │ jobs │ Img │ in │
├──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴────┤
│ Services Layer │
│ AnthropicService · ChatService · ToolService │
│ DatabaseService · MemoryService · SkillService │
@@ -78,6 +81,7 @@
| **Cron** | Cronos (Cron-Expression-Parsing) |
| **Frontend** | Vanilla JavaScript SPA (kein Framework) |
| **Markdown** | marked.js (Client-side Rendering) |
| **LaTeX** | KaTeX 0.16.21 (Math-Rendering, self-hosted mit woff2-Fonts) |
| **Editor** | CodeMirror 5 + EasyMDE |
| **Icons** | Font Awesome |
| **Fonts** | Inter, JetBrains Mono (self-hosted) |
@@ -120,7 +124,8 @@ gott/
│ ├── CronjobService.cs # BackgroundService für Cron-Tasks
│ ├── KreaService.cs # Krea Image Generation API
│ ├── ImageService.cs # Bild-Resize (Avatar, Session-Image)
│ └── StreamingTracker.cs # Concurrent Tracking aktiver Streams
│ ├── StreamingTracker.cs # Concurrent Tracking aktiver Streams
│ └── WorkspaceHelper.cs # Workspace-Pfad-Auflösung (relativ → absolut)
│
├── Endpoints/ # HTTP API (Minimal API)
│ ├── AuthEndpoint.cs # Login, Logout, Me
@@ -134,6 +139,7 @@ gott/
│ ├── KreaEndpoint.cs # Generate, List, File-Serve
│ ├── SkillEndpoint.cs # List, Agent-Skill-Mapping, Output-Files
│ ├── SharedEndpoint.cs # Nachrichten teilen (öffentliche Links)
│ ├── AdminEndpoint.cs # Admin: User-CRUD, Impersonate (Admin-only)
│ ├── ProfileEndpoint.cs # Profil, Persona, Passwort
│ ├── SettingsEndpoint.cs # User-Settings (JSON blob)
│ ├── BackupEndpoint.cs # Backup als tar.gz Download
@@ -158,16 +164,18 @@ gott/
│ │ ├── main.css # Sidebar, Layout-Struktur
│ │ ├── chat.css # Chat-Nachrichten, Bubbles
│ │ ├── dashboard.css # Dashboard-Grid
│ │ ├── form.css # Formulare, Inputs
│ │ ├── markdown.css # Markdown-Rendering-Styles
│ │ ├── form.css # Formulare, Inputs, Avatar-Editor-Modal
│ │ ├── markdown.css # Markdown + KaTeX Rendering-Styles
│ │ ├── file-explorer.css # File-Explorer Panel
│ │ ├── login.css # Login-Seite
│ │ ├── mobile.css # Responsive Anpassungen
│ │ ├── tooltip.css # Tooltip-Styles
│ │ ├── fonts.css # @font-face Definitionen
│ │ ├── placeholder.css # Placeholder/Loading States
│ │ ├── katex.min.css # KaTeX Math-Rendering Styles
│ │ ├── codemirror.min.css # CodeMirror Editor
│ │ └── easymde.min.css # EasyMDE Editor
│ │ ├── easymde.min.css # EasyMDE Editor
│ │ └── fonts/ # KaTeX woff2 Fonts (21 Dateien)
│ ├── js/
│ │ ├── app.js # App-Initialisierung, Auth-Check
│ │ ├── router.js # Client-side SPA Router
@@ -177,6 +185,7 @@ gott/
│ │ ├── history.js # History-Rendering, Pagination
│ │ ├── session.js # Session-Verwaltung
│ │ ├── file-explorer.js # File-Explorer Komponente
│ │ ├── avatar-editor.js # Wiederverwendbares Avatar-Editor-Modal
│ │ ├── commands-ui.js # Custom-Commands UI
│ │ ├── emoji.js # Emoji-Picker
│ │ ├── emoji.userdata.js # Häufig genutzte Emojis
@@ -189,6 +198,7 @@ gott/
│ │ │ ├── agent-page.js # Agent-Editor
│ │ │ ├── cronjob-page.js # Cronjob-Verwaltung
│ │ │ ├── profile-page.js # Profil-Bearbeitung
│ │ │ ├── admin-page.js # Admin: Benutzerverwaltung
│ │ │ ├── settings-page.js # App-Einstellungen
│ │ │ ├── login-page.js # Login-Formular
│ │ │ └── shared-page.js # Öffentliche geteilte Nachricht
@@ -197,6 +207,7 @@ gott/
│ │ ├── codemirror.min.js # Code Editor
│ │ ├── codemirror.cjs # CM CommonJS Wrapper
│ │ ├── easymde.min.js # Markdown Editor
│ │ ├── katex.min.js # KaTeX Math-Rendering Engine
│ │ └── cm-mode-*.min.js # CodeMirror Syntax-Modes
│ ├── fonts/ # Self-hosted Fonts (Inter, JetBrains Mono)
│ └── lib/font-awesome/ # Font Awesome Icons
@@ -251,10 +262,10 @@ Message (1) ─── (*) SharedMessage
| Tabelle | Beschreibung |
|-------------------|-----------------------------------------------------|
| `users` | Benutzer (username, password_hash, persona, settings) |
| `users` | Benutzer (username, password_hash, persona, settings, default_path) |
| `tokens` | Auth-Tokens (token → user_id) |
| `agents` | AI-Agenten (model, soul, identity, memory, tools) |
| `sessions` | Chat-Sessions (workspace, context, telegram-config) |
| `agents` | AI-Agenten (model, soul, identity, memory, tools, avatar_scale) |
| `sessions` | Chat-Sessions (workspace, context, telegram-config, avatar_scale, telegram_allowed_chat_ids) |
| `chains` | Request-Chains (model, tokens, iterations, timing) |
| `messages` | Einzelne Nachrichten (text, tool_use, tool_result) |
| `attachments` | Datei-Anhänge (base64, image/document) |
@@ -296,7 +307,7 @@ Die `Program.cs` ist der zentrale Einstiegspunkt und konfiguriert:
2. **Datenbank-Initialisierung** – `DatabaseService` erstellt Schema + Migrationen
3. **Default-User** – Wird automatisch angelegt falls keine User existieren
4. **Middleware** – Static Files → `AuthMiddleware` → Endpoints
5. **Endpoint-Mapping** – Alle 15 Endpoint-Gruppen registriert
5. **Endpoint-Mapping** – Alle 16 Endpoint-Gruppen registriert
6. **Server** – Kestrel auf `http://0.0.0.0:19000`
Initialisierungsreihenfolge:
@@ -317,6 +328,7 @@ AuthService → DatabaseService → EnsureDefaultUser
| `ChatService` | Singleton | Chat-Kontext aufbauen, History aus DB rekonstruieren |
| `AnthropicService` | Singleton | Claude API Kommunikation (Streaming SSE + Non-Streaming) |
| `ToolService` | Singleton | Tool-Definitionen bereitstellen, Tool-Aufrufe ausführen |
| `WorkspaceHelper` | Static | Workspace-Pfad-Auflösung (relativ → absolut), Path-Traversal-Schutz |
| `SystemPromptBuilder` | Static | Modularer System-Prompt aus konfigurierbaren Blöcken |
| `MemoryService` | Singleton | Agent-Gedächtnis auf Dateisystem (MEMORY.md, IDENTITY.md etc.) |
| `SkillService` | Singleton | Skill-Discovery und -Management aus Dateisystem |
@@ -337,7 +349,8 @@ Alle Endpoints folgen dem **Minimal API**-Pattern mit Extension Methods (`MapXxx
| **History** | `/api/history` | `GET history`, `GET poll`, `POST archive`, `GET archives`, `GET archive/{id}`, `GET attachments/{id}`, `PUT messages/{id}/mark`, `PUT messages/{id}/delete`, `GET chains/{id}` |
| **Sessions** | `/api/sessions/` | CRUD + `GET system-prompt`, `GET system-prompt-blocks`, `GET streaming`, `POST exec`, `GET git-status` |
| **Agents** | `/api/agents/` | CRUD |
| **Workspace** | `/api/workspace/` | `GET tree`, `GET/PUT file`, `GET browse`, `GET agent/tree`, `GET/PUT agent/file`, `GET raw` |
| **Admin** | `/api/admin/` | `GET users`, `POST users`, `PUT users/{id}`, `PUT users/{id}/password`, `DELETE users/{id}`, `POST users/{id}/impersonate` (Admin-only) |
| **Workspace** | `/api/workspace/` | `GET tree`, `GET/PUT file`, `GET browse`, `POST mkdir`, `DELETE dir`, `POST browse/mkdir`, `GET agent/tree`, `GET/PUT agent/file`, `POST agent/mkdir`, `GET raw` |
| **Telegram** | `/api/telegram/` | `POST webhook/{sessionId}` (unauthenticated), Session-Config CRUD, Webhook-Management |
| **Cronjobs** | `/api/cronjobs/` | CRUD + `POST toggle`, `POST run` |
| **Krea** | `/api/images/` | `POST generate`, `GET list`, `GET file/{name}`, `GET status` |
@@ -397,6 +410,7 @@ Das Frontend ist eine **Single-Page Application** ohne Build-Step, komplett in V
| `#/cronjobs` | `cronjob-page.js` | Cronjob-Verwaltung |
| `#/profile` | `profile-page.js` | Profil |
| `#/settings` | `settings-page.js` | App-Einstellungen |
| `#/admin` | `admin-page.js` | Benutzerverwaltung (Admin-only) |
| `#/login` | `login-page.js` | Login |
| `#/shared/{token}` | `shared-page.js` | Öffentliche geteilte Nachricht (kein Auth) |
@@ -405,10 +419,11 @@ Das Frontend ist eine **Single-Page Application** ohne Build-Step, komplett in V
- **app.js** – Initialisierung, Auth-Check, globale Event-Handler
- **api.js** – Zentraler API-Client mit `fetch()`-Wrapper, Auth-Redirect bei 401
- **ui.js** – UI-Utilities (Modals, Toasts, Confirmations, etc.)
- **chat.js** – SSE-Stream-Handling, Markdown-Rendering, Tool-Output-Darstellung
- **chat.js** – SSE-Stream-Handling, Markdown-Rendering (mit KaTeX), Tool-Output-Darstellung
- **history.js** – Chat-History laden, paginieren, rendern
- **session.js** – Session-State-Management
- **file-explorer.js** – Workspace File-Explorer Panel
- **file-explorer.js** – Workspace File-Explorer Panel (Dateien, Ordner erstellen/löschen, Folder-Browser)
- **avatar-editor.js** – Wiederverwendbares Avatar-Editor-Modal (Upload, Paste, AI-Generate, Scale)
- **commands-ui.js** – Custom-Commands UI-Komponente
- **emoji.js** – Emoji-Picker mit Kategorien und Suche
- **tooltip.js** – Tooltip-System für Hover-Infos
@@ -452,6 +467,7 @@ Der Chat-Flow von User-Input bis zur gerenderten Antwort:
│ • Tool-Results → nächste Iteration │
│ → Messages in DB persistieren │
│ → Stop wenn: end_turn / max_iterations│
│ → CancellationToken bei Abbruch │
└─────────────────────────────────────────┘
↓
5. Frontend: SSE Events empfangen und rendern
@@ -460,6 +476,15 @@ Der Chat-Flow von User-Input bis zur gerenderten Antwort:
• "done" → Token-Stats, Dauer, Model anzeigen
```
### Abbruch (Stop-Button)
Das `CancellationToken` wird durchgängig propagiert:
1. Frontend: `AbortController.abort()` → SSE-Fetch abgebrochen
2. ASP.NET: `RequestAborted` Token wird gecancelled
3. `AnthropicService`: `ThrowIfCancellationRequested()` am Loop-Anfang, vor/nach Tool-Execution
4. `WriteSseEvent()`: Alle SSE-Writes mit CancellationToken → sofortiger Abbruch bei Client-Disconnect
5. Chain wird mit `StopReason = "client_disconnected"` finalisiert
### Streaming vs Non-Streaming
- **Web-Chat**: Streaming via SSE (`text/event-stream`). Tokens werden in Echtzeit gerendert.
@@ -652,7 +677,8 @@ Sessions können mit einem **Telegram Bot** verbunden werden:
Pro Session werden gespeichert:
- `TelegramBotToken` – Bot-Token von @BotFather
- `TelegramWebhookSecret` – Geheimer Token für Webhook-Verifizierung
- `TelegramChatId` – Verknüpfte Chat-ID
- `TelegramChatId` – Verknüpfte Chat-ID (Owner)
- `TelegramAllowedChatIds` – Kommagetrennte Chat-ID-Whitelist für Zugriffskontrolle
### Webhook-Flow
@@ -664,13 +690,23 @@ Pro Session werden gespeichert:
4. AuthMiddleware lässt Webhook-Requests durch (kein Cookie nötig)
5. TelegramEndpoint prüft X-Telegram-Bot-Api-Secret-Token Header
6. TelegramService verarbeitet Update:
- /start → Session verknüpfen
- /start → Session verknüpfen (nur wenn noch kein Owner ODER Whitelist-Match)
- /clear → History archivieren
- /status → Session-Info anzeigen
- /unlink → Verknüpfung aufheben
- Text → An Claude weiterleiten (Non-Streaming)
```
### Zugriffskontrolle (Chat-ID-Whitelist)
Nicht jeder Telegram-User darf den Bot nutzen:
1. **Erster `/start`**: Kein Owner → User wird automatisch Owner (`TelegramChatId` gesetzt)
2. **Fremder `/start`**: User bekommt `🔒 Zugriff verweigert` + seine Chat-ID angezeigt
3. **Owner-Benachrichtigung**: Owner erhält `🔔 Zugriffsanfrage` mit Username + Chat-ID
4. **Freigabe**: Owner/Admin trägt Chat-ID in Session-Settings ein (`TelegramAllowedChatIds`)
5. **Prüfung**: Alle Commands + Nachrichten prüfen via `IsChatIdAllowed()` (Owner + Whitelist)
### Non-Streaming Chat
Telegram-Nachrichten werden über `AnthropicService.ChatNonStreaming()` verarbeitet. Die History wird genauso aus der DB aufgebaut wie beim Web-Chat. Antworten über 4096 Zeichen werden automatisch in Chunks aufgeteilt.
@@ -760,7 +796,18 @@ chatgpt-image, runway-gen-4
- Agents sind an `user_id` gebunden
- Cronjobs sind an `user_id` gebunden
- Jeder User sieht nur seine eigenen Daten
- Admin-Flag (`is_admin`) für Backup-Endpoint
- Admin-Flag (`is_admin`) für Backup-Endpoint, Admin-Benutzerverwaltung, Impersonate
### Default-Path (Base-Path)
Jeder User hat einen optionalen `DefaultPath` – den Basis-Ordner für alle Workspaces. Wird **nur vom Admin** gesetzt.
- **Session-Workspace**: Speichert nur den **relativen Pfad** zum DefaultPath (z.B. `myapp` statt `/home/user/projects/myapp`)
- **Auflösung**: `WorkspaceHelper.ResolveWorkspace()` kombiniert DefaultPath + relativen Workspace → absoluter Pfad
- **System-Prompt**: Bekommt den **aufgelösten absoluten Pfad**, damit der Agent korrekt navigieren kann
- **Path-Traversal-Schutz**: `..` in relativen Workspace-Pfaden wird blockiert (400 Bad Request)
- **Legacy-Support**: Absolute Pfade in `session.Workspace` funktionieren weiterhin als Fallback
- **Folder-Browser**: Startet beim DefaultPath, User kann nicht über den Base-Path hinaus navigieren
### Default-User
@@ -772,6 +819,37 @@ Jeder User hat eine optionale **Persona** – ein Freitext der beschreibt wer er
---
## Admin-Benutzerverwaltung
Admins können alle User verwalten über `AdminEndpoint.cs` (alle Endpoints erfordern `is_admin`).
### API-Endpunkte
| Methode | Route | Beschreibung |
|----------|-----------------------------------------|-------------------------------------------|
| `GET` | `/api/admin/users` | Alle User auflisten |
| `POST` | `/api/admin/users` | Neuen User erstellen |
| `PUT` | `/api/admin/users/{id}` | User bearbeiten (Name, Username, Admin, DefaultPath) |
| `PUT` | `/api/admin/users/{id}/password` | Passwort zurücksetzen |
| `DELETE` | `/api/admin/users/{id}` | User löschen (+ Tokens, Shared Messages) |
| `POST` | `/api/admin/users/{id}/impersonate` | Als anderer User einloggen (Login-as-User) |
### Impersonate (Login-as-User)
Admins können sich als beliebiger User einloggen ohne dessen Passwort zu kennen:
1. `POST /api/admin/users/{id}/impersonate` → Erstellt Token für Zieluser
2. Setzt neuen `gott_token`-Cookie → Admin-Session wird zum Zieluser
3. `location.reload()` → Frontend lädt mit neuem User-Kontext
### Frontend (`admin-page.js`)
- Erreichbar über Dashboard-Link "Benutzerverwaltung" (nur für Admins sichtbar)
- User-Liste als Cards mit Edit/Delete/Impersonate-Buttons
- Inline-Formular für Create/Edit
- Sich selbst löschen ist nicht möglich
---
## Nachrichten teilen (Shared Messages)
Einzelne Nachrichten oder ganze Threads können als **öffentliche Links** geteilt werden – ohne Login aufrufbar.
@@ -818,23 +896,37 @@ Einzelne Nachrichten oder ganze Threads können als **öffentliche Links** getei
### Workspace
Eine Session kann einen **Workspace-Pfad** haben (z.B. `/app/gott`). Dieser:
Eine Session kann einen **Workspace-Pfad** haben. Dieser wird **relativ** zum `DefaultPath` des Users gespeichert (z.B. `myapp`) und via `WorkspaceHelper.ResolveWorkspace()` zum absoluten Pfad aufgelöst. Der Workspace:
- Aktiviert die Datei-/Shell-Tools für den Agent
- Ermöglicht den File-Explorer im Frontend
- Bietet `git status` Informationen
- Erlaubt das Ausführen von Shell-Befehlen (foreground + background)
### WorkspaceHelper
`Services/WorkspaceHelper.cs` – Statische Hilfsklasse für Workspace-Pfade:
| Methode | Beschreibung |
|----------------------------|-----------------------------------------------------------|
| `ResolveWorkspace(session, user)` | Relativer Workspace + DefaultPath → absoluter Pfad |
| `ValidateRelativeWorkspace(workspace)` | Prüft auf `..`, `~`, absolute Pfade → null bei ungültig |
| `ToRelativeWorkspace(absolute, defaultPath)` | Absoluter Pfad → relativ zum DefaultPath |
### File-Explorer API
| Endpoint | Beschreibung |
|-----------------------------------|----------------------------------------|
| `GET /api/workspace/tree` | Verzeichnisbaum (mit Ignore-Filtern) |
| `GET /api/workspace/file` | Dateiinhalt lesen (max 2MB) |
| `PUT /api/workspace/file` | Datei speichern |
| `GET /api/workspace/browse` | Verzeichnis-Browser (für Pfad-Auswahl)|
| `GET /api/workspace/raw` | Raw-Datei ausliefern (Bilder, PDFs) |
| `GET /api/workspace/agent/tree` | Agent-Memory-Verzeichnis |
| `GET/PUT /api/workspace/agent/file` | Agent-Memory-Dateien lesen/schreiben |
| Endpoint | Beschreibung |
|-------------------------------------|------------------------------------------|
| `GET /api/workspace/tree` | Verzeichnisbaum (mit Ignore-Filtern) |
| `GET /api/workspace/file` | Dateiinhalt lesen (max 2MB) |
| `PUT /api/workspace/file` | Datei speichern |
| `POST /api/workspace/mkdir` | Ordner erstellen im Workspace |
| `DELETE /api/workspace/dir` | Leeren Ordner löschen im Workspace |
| `GET /api/workspace/browse` | Verzeichnis-Browser (für Pfad-Auswahl) |
| `POST /api/workspace/browse/mkdir` | Ordner erstellen per absolutem Pfad |
| `GET /api/workspace/raw` | Raw-Datei ausliefern (Bilder, PDFs) |
| `GET /api/workspace/agent/tree` | Agent-Memory-Verzeichnis |
| `GET/PUT /api/workspace/agent/file` | Agent-Memory-Dateien lesen/schreiben |
| `POST /api/workspace/agent/mkdir` | Ordner erstellen im Agent-Memory |
### Ignored Directories
@@ -848,6 +940,77 @@ Eine Session kann einen **Workspace-Pfad** haben (z.B. `/app/gott`). Dieser:
---
## Avatar-System
Sessions und Agents können ein **Bild/Avatar** haben mit konfigurierbarem Zoom.
### Datenfelder
| Feld | Tabellen | Beschreibung |
|------|----------|-------------|
| `image` / `avatar` | `sessions` / `agents` | Base64-kodiertes Bild (JPEG, max 512px via ImageService) |
| `avatar_scale` | `sessions`, `agents` | Zoom-Faktor (0.5× – 3×, Default 1.0), angewandt via CSS `transform: scale()` |
### Avatar-Editor (`avatar-editor.js`)
Wiederverwendbares Modal-Modul, genutzt in Session- und Agent-Pages:
```javascript
const editor = AvatarEditor.init({
previewEl: document.querySelector('.avatar-preview'),
currentImage: existingBase64OrNull,
currentScale: 1.0,
onImageChange: (base64) => { /* Preview aktualisieren */ },
onScaleChange: (scale) => { /* Live-Preview skalieren */ }
});
// Werte auslesen: editor.getImage(), editor.getScale()
// Aufräumen: editor.destroy()
```
**Features:**
- **Upload**: Datei-Input für Bild-Upload
- **Paste**: Ctrl+V → Bild aus Zwischenablage einfügen
- **AI-Generate**: Text-Prompt → Krea API → Bild generieren (nur wenn `KreaToken` konfiguriert)
- **Scale**: Range-Slider 0.5× – 3× mit Live-Preview
- **Clear**: Bild entfernen
### Dashboard-Darstellung
Cards zeigen Avatare mit `transform: scale(avatarScale)` im `.card-image` Container (`overflow: hidden`).
---
## LaTeX/Math-Rendering
Chat-Nachrichten unterstützen **LaTeX-Formeln** via KaTeX:
### Syntax
- `$$...$$` → **Block-Math** (zentriert, eigene Zeile)
- `$...$` → **Inline-Math** (im Fließtext)
### Rendering-Pipeline (in `ui.js` `renderMarkdown()`)
```
1. Code-Blöcke extrahieren und schützen (Platzhalter)
2. Block-LaTeX ($$...$$) extrahieren und schützen
3. Inline-LaTeX ($...$) extrahieren und schützen
4. Markdown-Rendering via marked.js
5. Code-Blöcke wiederherstellen
6. LaTeX-Platzhalter durch KaTeX.renderToString() ersetzen
7. Bei fehlerhaftem LaTeX: Fallback als <code class="katex-error">
```
### Abhängigkeiten
- `wwwroot/js/lib/katex.min.js` – KaTeX Engine (v0.16.21)
- `wwwroot/css/katex.min.css` – Styles
- `wwwroot/css/fonts/` – 21 woff2-Fonts (self-hosted)
Funktioniert überall wo `renderMarkdown()` aufgerufen wird: Chat, Streaming, History, Archiv, Shared Page.
---
## Windows Desktop App
Im `WindowsApp/`-Ordner liegt ein minimaler **WPF WebView2 Client**: