Update LemonSpace Manifest to v2.1, enabling all 9 image models in OpenRouter with server-side tier enforcement. Enhance dashboard functionality with a bundled snapshot query and localStorage caching for improved performance and analytics. Introduce credits activity chart and optimize canvas graph queries for better data handling.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# 🍋 LemonSpace — Produkt-Manifest
|
# 🍋 LemonSpace — Produkt-Manifest
|
||||||
|
|
||||||
**v2.0 — April 2026**
|
**v2.1 — April 2026**
|
||||||
|
|
||||||
*Self-Hosted, Source-Available Creative Workspace*
|
*Self-Hosted, Source-Available Creative Workspace*
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ Kompakt statt erschöpfend. Details wandern in eigene Architecture Decision Reco
|
|||||||
|-------|-------------|--------|
|
|-------|-------------|--------|
|
||||||
| Backend | Convex (self-hosted). Bewusster Lock-in für Realtime, Storage, Jobs. Migrations-Pfad: Convex Cloud EU. | ✅ |
|
| Backend | Convex (self-hosted). Bewusster Lock-in für Realtime, Storage, Jobs. Migrations-Pfad: Convex Cloud EU. | ✅ |
|
||||||
| Auth | Better Auth + Magic Link (via polar-sh/better-auth plugin) | ✅ |
|
| Auth | Better Auth + Magic Link (via polar-sh/better-auth plugin) | ✅ |
|
||||||
| AI Layer | OpenRouter als primäre AI-Schicht. 9 Image-Modelle, Phase 1: nur Gemini 2.5 Flash aktiv. | ✅ |
|
| AI Layer | OpenRouter als primäre AI-Schicht. Alle 9 Image-Modelle aktiv, serverseitiges Tier-Enforcement, tier-aware Model Selector. | ✅ |
|
||||||
| Self-hosted KI | rembg, Real-ESRGAN, GFPGAN — kostenlos, separate Repos | ✅ |
|
| Self-hosted KI | rembg, Real-ESRGAN, GFPGAN — kostenlos, separate Repos | ✅ |
|
||||||
| Payment | Polar.sh (MoR, VAT, Better Auth Plugin @polar-sh/better-auth) | ✅ |
|
| Payment | Polar.sh (MoR, VAT, Better Auth Plugin @polar-sh/better-auth) | ✅ |
|
||||||
| Credits | Reservation + Commit. Credit-Abstraktion (1 Cr = €0,01 OR intern). Markup: 2× Bild, 2,5–3× Agent. | ✅ |
|
| Credits | Reservation + Commit. Credit-Abstraktion (1 Cr = €0,01 OR intern). Markup: 2× Bild, 2,5–3× Agent. | ✅ |
|
||||||
@@ -160,7 +160,7 @@ Fokus heißt Nein sagen. Diese Features sind bewusst ausgeklammert, nicht verges
|
|||||||
| Team-Features | Workspaces, Rollen, Rechte, Seat-Management — erst wenn Business-Tier validiert. |
|
| Team-Features | Workspaces, Rollen, Rechte, Seat-Management — erst wenn Business-Tier validiert. |
|
||||||
| docker-compose.yml | Self-Hosting dokumentieren, aber nicht den Hosted-MVP verzögern. |
|
| docker-compose.yml | Self-Hosting dokumentieren, aber nicht den Hosted-MVP verzögern. |
|
||||||
| E2E-Testing | Neubewertung bei Skalierung. |
|
| E2E-Testing | Neubewertung bei Skalierung. |
|
||||||
| Modellauswahl-UI | Phase 1 nur ein Modell (Gemini 2.5 Flash). Auswahl-UI folgt in Phase 2. |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -235,10 +235,11 @@ Priorisiert nach Abhängigkeiten. Jeder Schritt hat ein klares Artefakt.
|
|||||||
| 6 | Better Auth + Polar + Credit-System | Login, Checkout, Balance-Tracking, Reservation+Commit | ✅ |
|
| 6 | Better Auth + Polar + Credit-System | Login, Checkout, Balance-Tracking, Reservation+Commit | ✅ |
|
||||||
| 7 | Polar Webhook-Handling | Subscription-Events, automatische Credit-Zuweisung | ✅ |
|
| 7 | Polar Webhook-Handling | Subscription-Events, automatische Credit-Zuweisung | ✅ |
|
||||||
| 8 | WebGL Image Pipeline | Adjustment-Nodes mit GLSL-Shadern | ✅ |
|
| 8 | WebGL Image Pipeline | Adjustment-Nodes mit GLSL-Shadern | ✅ |
|
||||||
| 9 | Vollständige OpenRouter Integration | Alle 9 Modelle + Modellauswahl-UI | ☐ |
|
| 9 | Vollständige OpenRouter Integration | Alle 9 Modelle + Modellauswahl-UI | ✅ |
|
||||||
| 10 | Agent Node | Analyse, Clarification, Execution, Output | ☐ |
|
| 10 | Agent Node | Analyse, Clarification, Execution, Output | ☐ |
|
||||||
| 11 | Self-hosted KI-Services | rembg, Real-ESRGAN, GFPGAN | ☐ |
|
| 11 | Self-hosted KI-Services | rembg, Real-ESRGAN, GFPGAN | ☐ |
|
||||||
| 12 | docker-compose.yml + Setup-README | Self-Hosting-Anleitung | ☐ |
|
| 12 | docker-compose.yml + Setup-README | Self-Hosting-Anleitung | ☐ |
|
||||||
|
| 13 | Dashboard Snapshot + Analytics | Gebündelte Query, localStorage-Cache, Credits-Activity-Chart | ✅ |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -257,4 +258,4 @@ Folgende Themen werden in eigenen Dokumenten vertieft. Das Manifest bleibt schla
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*LemonSpace Manifest v2.0 — April 2026*
|
*LemonSpace Manifest v2.1 — April 2026*
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
| Version | Status | Datum | Projekt |
|
| Version | Status | Datum | Projekt |
|
||||||
|---------|--------|-------|---------|
|
|---------|--------|-------|---------|
|
||||||
| v2.0 | Draft | April 2026 | lemonspace.app |
|
| v2.1 | Draft | April 2026 | lemonspace.app |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
| v1.4 | Bildbearbeitung: Neue Kategorie 4 „Bildbearbeitung" mit non-destruktivem Adjustment-Stack (zwischen Transformation und Steuerung). 4 Adjustment-Nodes (Kurven, Farbe, Licht, Detail) + Render-Node. Alle Operationen credit-frei (client-seitig via Canvas API / WebGL). Steuerung → Kat. 5, Canvas & Layout → Kat. 6. Phase 2. |
|
| v1.4 | Bildbearbeitung: Neue Kategorie 4 „Bildbearbeitung" mit non-destruktivem Adjustment-Stack (zwischen Transformation und Steuerung). 4 Adjustment-Nodes (Kurven, Farbe, Licht, Detail) + Render-Node. Alle Operationen credit-frei (client-seitig via Canvas API / WebGL). Steuerung → Kat. 5, Canvas & Layout → Kat. 6. Phase 2. |
|
||||||
| v1.5 | Stage 3 Offline Sync: Local-First Canvas mit IndexedDB Queue, Optimistic Updates, ID-Remapping. Magic Link Auth via Better Auth Plugin. react-resizable-panels für Sidebar Resizing. Canvas Modularisierung, Dashboard Dialoge, Auth Race-Härtung.|
|
| v1.5 | Stage 3 Offline Sync: Local-First Canvas mit IndexedDB Queue, Optimistic Updates, ID-Remapping. Magic Link Auth via Better Auth Plugin. react-resizable-panels für Sidebar Resizing. Canvas Modularisierung, Dashboard Dialoge, Auth Race-Härtung.|
|
||||||
| v2.0 | **Phase-1-Umfang erweitert:** Video- und Asset-Nodes vorgezogen (Phase 2→1). Bildbearbeitungs-Nodes (Kurven, Farbe, Licht, Detail, Render) vorgezogen (Phase 2→1). Vollständige WebGL-basierte Image-Pipeline implementiert (`lib/image-pipeline/`). Node-Taxonomie hat 6 Kategorien mit 27 Node-Typen. Phase-1-Status-Tabelle aktualisiert. |
|
| v2.0 | **Phase-1-Umfang erweitert:** Video- und Asset-Nodes vorgezogen (Phase 2→1). Bildbearbeitungs-Nodes (Kurven, Farbe, Licht, Detail, Render) vorgezogen (Phase 2→1). Vollständige WebGL-basierte Image-Pipeline implementiert (`lib/image-pipeline/`). Node-Taxonomie hat 6 Kategorien mit 27 Node-Typen. Phase-1-Status-Tabelle aktualisiert. |
|
||||||
|
| v2.1 | **Alle 9 Image-Modelle aktiviert (Phase 2→1):** Vollständige OpenRouter Image Gen Integration mit serverseitigem Tier-Enforcement und modellspezifischen Request-Modalities. Tier-aware Model Selector im Prompt-Node. AI-Modularisierung: `ai_errors.ts`, `ai_node_data.ts`, `ai_retry.ts` aus `ai.ts` extrahiert. Dashboard Snapshot Cache (`convex/dashboard.ts`): Gebündelte Query mit localStorage-Cache (`lib/dashboard-snapshot-cache.ts`). Credits Activity Analytics: `lib/credits-activity.ts` + `CreditsActivityChart` (Recharts). Canvas Graph Query Cache (`convex/canvasGraph.ts` + `canvas-graph-query-cache.ts`): Performance-Optimierung durch separaten Graph-Endpunkt mit Optimistic Store. Neuer Hook `use-dashboard-snapshot.ts`. ShadCN Chart-Komponente (`components/ui/chart.tsx`) + Recharts 3.8. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -238,6 +239,7 @@ Agent Nodes sind ein spezieller Node-Typ auf dem Canvas. Sie fungieren als Smart
|
|||||||
| Image Pipeline | WebGL + GLSL Shaders | Hardware-beschleunigte Bildverarbeitung im Browser |
|
| Image Pipeline | WebGL + GLSL Shaders | Hardware-beschleunigte Bildverarbeitung im Browser |
|
||||||
| Offline Sync | IndexedDB + localStorage | Canvas-Sync-Queue, Snapshot-Persistenz, Optimistic Updates |
|
| Offline Sync | IndexedDB + localStorage | Canvas-Sync-Queue, Snapshot-Persistenz, Optimistic Updates |
|
||||||
| Package Manager | pnpm | Je Repo |
|
| Package Manager | pnpm | Je Repo |
|
||||||
|
| Charts / Visualization | Recharts + ShadCN Chart | v3.8.0 — Dashboard Credits Activity Chart |
|
||||||
|
|
||||||
### Zwei-Repo-Strategie
|
### Zwei-Repo-Strategie
|
||||||
|
|
||||||
@@ -295,13 +297,23 @@ Dokumentierter Migrationspfad bei Skalierung: Convex Cloud mit EU-Standort. Conv
|
|||||||
| Gemini 3 Pro Image | google/gemini-3-pro-image-preview | Multi-Image, 4K, bestes Text-Rendering | ~€0,08–0,15 |
|
| Gemini 3 Pro Image | google/gemini-3-pro-image-preview | Multi-Image, 4K, bestes Text-Rendering | ~€0,08–0,15 |
|
||||||
| GPT-5 Image | openai/gpt-5-image | Instruction Following, Text in Bild | ~€0,10–0,20 |
|
| GPT-5 Image | openai/gpt-5-image | Instruction Following, Text in Bild | ~€0,10–0,20 |
|
||||||
|
|
||||||
### Aktuell aktiviertes Modell (Phase 1)
|
### Aktuell aktivierte Modelle (Phase 1)
|
||||||
|
|
||||||
| Modell | ID | Credits | Tier-Zugang |
|
Alle 9 Image-Modelle sind aktiviert. Server-seitiges Tier-Enforcement prüft `minTier` pro Modell. Der Prompt-Node bietet einen tier-aware Model Selector.
|
||||||
|--------|-----|---------|-------------|
|
|
||||||
| Gemini 2.5 Flash Image | google/gemini-2.5-flash-image | 4 Cr | Alle Tiers |
|
|
||||||
|
|
||||||
> Weitere Modelle werden in Phase 2 freigeschaltet (Modellauswahl-UI im Canvas).
|
| Modell | ID | Credits | Tier-Zugang | Modalities |
|
||||||
|
|--------|-----|---------|-------------|------------|
|
||||||
|
| Gemini 2.5 Flash | google/gemini-2.5-flash-image | 4 Cr | Alle Tiers | image + text |
|
||||||
|
| FLUX.2 Klein 4B | black-forest-labs/flux.2-klein-4b | 2 Cr | Alle Tiers | image only |
|
||||||
|
| Seedream 4.5 | bytedance-seed/seedream-4.5 | 5 Cr | Alle Tiers | image only |
|
||||||
|
| Gemini 3.1 Flash Image | google/gemini-3.1-flash-image-preview | 6 Cr | Alle Tiers | image + text |
|
||||||
|
| GPT-5 Image Mini | openai/gpt-5-image-mini | 8 Cr | Ab Starter | image + text |
|
||||||
|
| Riverflow V2 Fast | sourceful/riverflow-v2-fast | 9 Cr | Ab Starter | image only |
|
||||||
|
| Riverflow V2 Pro | sourceful/riverflow-v2-pro | 12 Cr | Ab Starter | image only |
|
||||||
|
| Gemini 3 Pro Image | google/gemini-3-pro-image-preview | 13 Cr | Ab Starter | image + text |
|
||||||
|
| GPT-5 Image | openai/gpt-5-image | 15 Cr | Ab Starter | image + text |
|
||||||
|
|
||||||
|
> **Request Modalities:** Modelle mit `image only` senden `modalities: ["image"]`, alle anderen `modalities: ["image", "text"]`. Dies wird in `convex/openrouter.ts` über das `requestModalities`-Feld gesteuert.
|
||||||
|
|
||||||
### Self-hosted Services
|
### Self-hosted Services
|
||||||
|
|
||||||
@@ -466,12 +478,15 @@ Credits = ROUND(API-Kosten × Markup ÷ Kurs). Agent-Calls haben höheren Markup
|
|||||||
|
|
||||||
| Operation | Modell | API-Kosten | Markup | Credits | Tier-Zugang |
|
| Operation | Modell | API-Kosten | Markup | Credits | Tier-Zugang |
|
||||||
|-----------|--------|------------|--------|---------|-------------|
|
|-----------|--------|------------|--------|---------|-------------|
|
||||||
| Bildgenerierung (Standard) | Gemini 2.5 Flash Image | ~€0,04 | 2× | 4 Cr | Alle Tiers |
|
| Bildgenerierung (Standard) | Gemini 2.5 Flash | ~€0,04 | 1× | 4 Cr | Alle Tiers |
|
||||||
| Bildgenerierung (Budget) | FLUX.2 Klein 4B | ~€0,02 | 2× | 4 Cr | Alle Tiers |
|
| Bildgenerierung (Budget) | FLUX.2 Klein 4B | ~€0,02 | 1× | 2 Cr | Alle Tiers |
|
||||||
| Bildgenerierung (Standard+) | Gemini 3.1 Flash Image | ~€0,06 | 2× | 12 Cr | Alle Tiers |
|
| Bildgenerierung (Standard) | Seedream 4.5 | ~€0,05 | 1× | 5 Cr | Alle Tiers |
|
||||||
| Bildgenerierung (Premium) | GPT-5 Image Mini | ~€0,08 | 2× | 16 Cr | Ab Starter |
|
| Bildgenerierung (Standard+) | Gemini 3.1 Flash Image | ~€0,06 | 1× | 6 Cr | Alle Tiers |
|
||||||
| Bildgenerierung (Ultra) | GPT-5 Image | ~€0,18 | 2× | 36 Cr | Ab Starter |
|
| Bildgenerierung (Premium) | GPT-5 Image Mini | ~€0,08 | 1× | 8 Cr | Ab Starter |
|
||||||
| Bildgen. (Pro Text/4K) | Riverflow V2 Pro | ~€0,33 | 1,5× | 50 Cr | Ab Starter |
|
| Bildgenerierung (Premium) | Riverflow V2 Fast | ~€0,09 | 1× | 9 Cr | Ab Starter |
|
||||||
|
| Bildgenerierung (Premium) | Riverflow V2 Pro | ~€0,12 | 1× | 12 Cr | Ab Starter |
|
||||||
|
| Bildgenerierung (Premium) | Gemini 3 Pro Image | ~€0,13 | 1× | 13 Cr | Ab Starter |
|
||||||
|
| Bildgenerierung (Ultra) | GPT-5 Image | ~€0,15 | 1× | 15 Cr | Ab Starter |
|
||||||
| Agent Reasoning (leicht) | Claude Sonnet | ~€0,03 | 3× | 9 Cr | Ab Starter |
|
| Agent Reasoning (leicht) | Claude Sonnet | ~€0,03 | 3× | 9 Cr | Ab Starter |
|
||||||
| Agent Reasoning (mittel) | Claude Sonnet | ~€0,06 | 2,5× | 15 Cr | Ab Starter |
|
| Agent Reasoning (mittel) | Claude Sonnet | ~€0,06 | 2,5× | 15 Cr | Ab Starter |
|
||||||
| Agent-Run (komplex) | Multi-Step Workflow | ~€0,15 | 2,5× | 38 Cr | Ab Starter |
|
| Agent-Run (komplex) | Multi-Step Workflow | ~€0,15 | 2,5× | 38 Cr | Ab Starter |
|
||||||
@@ -590,6 +605,12 @@ Agent Status: analyzing
|
|||||||
| Video Browser | ✅ Erledigt |
|
| Video Browser | ✅ Erledigt |
|
||||||
| Connection Policy (Edge-Validierung) | ✅ Erledigt |
|
| Connection Policy (Edge-Validierung) | ✅ Erledigt |
|
||||||
| Adjustment Presets (Built-in + User-defined) | ✅ Erledigt |
|
| Adjustment Presets (Built-in + User-defined) | ✅ Erledigt |
|
||||||
|
| Vollständige OpenRouter Image Gen (alle 9 Modelle + Tier-Enforcement) | ✅ Erledigt |
|
||||||
|
| Tier-aware Model Selector im Prompt-Node | ✅ Erledigt |
|
||||||
|
| Dashboard Snapshot Cache (gebündelte Query + localStorage) | ✅ Erledigt |
|
||||||
|
| Credits Activity Analytics (Recharts Chart) | ✅ Erledigt |
|
||||||
|
| Canvas Graph Query Cache (Performance-Optimierung) | ✅ Erledigt |
|
||||||
|
| AI Modularisierung (`ai_errors.ts`, `ai_retry.ts`, `ai_node_data.ts`) | ✅ Erledigt |
|
||||||
| docker-compose.yml + .env.example + Setup-README | ☐ Offen |
|
| docker-compose.yml + .env.example + Setup-README | ☐ Offen |
|
||||||
|
|
||||||
### Phase 2 — KI-Features
|
### Phase 2 — KI-Features
|
||||||
@@ -605,8 +626,6 @@ Agent Status: analyzing
|
|||||||
|
|
||||||
| Task | Status |
|
| Task | Status |
|
||||||
|------|--------|
|
|------|--------|
|
||||||
| Vollständige OpenRouter Image Gen Integration (alle 9 Modelle) | ☐ Offen |
|
|
||||||
| Experten-Modus: Modellauswahl-UI im Canvas AI Panel | ☐ Offen |
|
|
||||||
| OpenRouter Text/Reasoning Integration (Claude 3.5 Sonnet) | ☐ Offen |
|
| OpenRouter Text/Reasoning Integration (Claude 3.5 Sonnet) | ☐ Offen |
|
||||||
| Agent Node: Analyse, Clarification, Execution, Output | ☐ Offen |
|
| Agent Node: Analyse, Clarification, Execution, Output | ☐ Offen |
|
||||||
| Skeleton-Nodes: Platzierung nach Plan-Erstellung | ☐ Offen |
|
| Skeleton-Nodes: Platzierung nach Plan-Erstellung | ☐ Offen |
|
||||||
@@ -632,7 +651,7 @@ Agent Status: analyzing
|
|||||||
| Versions-History | ☐ Offen |
|
| Versions-History | ☐ Offen |
|
||||||
| Weitere Agent Templates | ☐ Offen |
|
| Weitere Agent Templates | ☐ Offen |
|
||||||
| Export-Funktionen (PNG, PDF, ZIP) | ☐ Offen |
|
| Export-Funktionen (PNG, PDF, ZIP) | ☐ Offen |
|
||||||
| Performance-Optimierung für große Canvases | ☐ Offen |
|
| Performance-Optimierung für große Canvases | ✅ Erledigt |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -646,7 +665,7 @@ Agent Status: analyzing
|
|||||||
| Payment Provider | ✅ Polar (Merchant of Record, VAT-Handling) |
|
| Payment Provider | ✅ Polar (Merchant of Record, VAT-Handling) |
|
||||||
| Self-Hosting-Strategie | ✅ docker-compose.yml + .env.example + README |
|
| Self-Hosting-Strategie | ✅ docker-compose.yml + .env.example + README |
|
||||||
| Convex Lock-in | ✅ Bewusst akzeptiert; Migrations-Pfad: Convex Cloud EU |
|
| Convex Lock-in | ✅ Bewusst akzeptiert; Migrations-Pfad: Convex Cloud EU |
|
||||||
| OpenRouter Image-Modelle | ✅ 9 Modelle definiert, Phase 1: nur Gemini 2.5 Flash aktiv |
|
| OpenRouter Image-Modelle | ✅ Alle 9 Modelle aktiv mit serverseitigem Tier-Enforcement |
|
||||||
| Lizenz | ✅ BSL 1.1, 3 Jahre Change Date, Apache 2.0 |
|
| Lizenz | ✅ BSL 1.1, 3 Jahre Change Date, Apache 2.0 |
|
||||||
| Repo-Strategie | ✅ Zwei unabhängige Repos, Auth-Cookie-Sharing |
|
| Repo-Strategie | ✅ Zwei unabhängige Repos, Auth-Cookie-Sharing |
|
||||||
| Job Queue | ✅ Convex native (Phase 1), externe Lösung bei Bedarf |
|
| Job Queue | ✅ Convex native (Phase 1), externe Lösung bei Bedarf |
|
||||||
@@ -721,4 +740,4 @@ Die Software wird unter der Business Source License 1.1 (BSL 1.1) veröffentlich
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*LemonSpace PRD v2.0 — April 2026*
|
*LemonSpace PRD v2.1 — April 2026*
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ app/(app)/canvas/[canvasId]/page.tsx
|
|||||||
| `canvas-delete-handlers.ts` | Hook für `onBeforeDelete`, `onNodesDelete`, `onEdgesDelete` inkl. Bridge-Edges |
|
| `canvas-delete-handlers.ts` | Hook für `onBeforeDelete`, `onNodesDelete`, `onEdgesDelete` inkl. Bridge-Edges |
|
||||||
| `canvas-reconnect.ts` | Hook für Edge-Reconnect (`onReconnectStart`, `onReconnect`, `onReconnectEnd`) |
|
| `canvas-reconnect.ts` | Hook für Edge-Reconnect (`onReconnectStart`, `onReconnect`, `onReconnectEnd`) |
|
||||||
| `canvas-media-utils.ts` | Media-Helfer wie `getImageDimensions(file)` |
|
| `canvas-media-utils.ts` | Media-Helfer wie `getImageDimensions(file)` |
|
||||||
|
| `use-canvas-data.ts` | Hook: Bündelt Canvas-Graph-Query, Storage-URL-Auflösung und Auth-State in einer einzigen Abstraktion |
|
||||||
|
| `canvas-graph-query-cache.ts` | Optimistic Store Helper für `canvasGraph.get` (getNodes, getEdges, setNodes, setEdges) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -231,6 +233,39 @@ Im **Light Mode** wird der eigentliche Edge-`stroke` ebenfalls aus dieser Akzent
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Canvas Graph Query Cache
|
||||||
|
|
||||||
|
Performance-Optimierung: Statt separater Queries für Nodes und Edges nutzt der Canvas eine einzige gebündelte Query (`canvasGraph.get`), die über einen Optimistic Store Layer läuft.
|
||||||
|
|
||||||
|
**Architektur:**
|
||||||
|
```
|
||||||
|
useCanvasData (use-canvas-data.ts)
|
||||||
|
├── canvasGraphQuery (canvasGraph.get) ← Einzelne gebündelte Query
|
||||||
|
├── canvasGraphQueryCache (Optimistic Store Helper)
|
||||||
|
│ ├── getCanvasGraphNodesFromQuery()
|
||||||
|
│ ├── getCanvasGraphEdgesFromQuery()
|
||||||
|
│ ├── setCanvasGraphNodesInQuery()
|
||||||
|
│ └── setCanvasGraphEdgesInQuery()
|
||||||
|
├── Storage URL Resolution (batchGetUrlsForCanvas)
|
||||||
|
└── Auth State (authClient.useSession + useConvexAuth)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Vorteil:** Optimistic Updates (Node-Erstellung, Edge-Erstellung etc.) aktualisieren den Optimistic Store direkt, ohne auf die Server-Bestätigung warten zu müssen. Die separaten Node/Edge-Queries wurden durch diesen Ansatz abgelöst.
|
||||||
|
|
||||||
|
**`use-canvas-data.ts`:**
|
||||||
|
- Kapselt den gesamten Canvas-Datenfluss: Graph-Query → Storage-URLs → fertige Daten
|
||||||
|
- `shouldSkipCanvasQueries` verhindert API-Calls vor Auth-Ready
|
||||||
|
- `storageIdsForCanvas` extrahiert Storage-IDs aus Nodes und löst sie via `batchGetUrlsForCanvas` auf
|
||||||
|
- Development-Logging für Auth-State-Debugging
|
||||||
|
|
||||||
|
**`canvas-graph-query-cache.ts`:**
|
||||||
|
- Typisierter Zugriff auf den Convex Optimistic Local Store
|
||||||
|
- `canvasGraphQuery` — Typ-safe Reference auf `api.canvasGraph.get`
|
||||||
|
- `getCanvasGraphNodesFromQuery/EdgesFromQuery` — Lesen
|
||||||
|
- `setCanvasGraphNodesInQuery/EdgesInQuery` — Schreiben (für Optimistic Updates)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Wichtige Gotchas
|
## Wichtige Gotchas
|
||||||
|
|
||||||
- **`data.url` vs `storageId`:** Node-Komponenten erhalten `data.url` (aufgelöste HTTP-URL), nicht `storageId` direkt. Die URL wird von `convexNodeDocWithMergedStorageUrl` injiziert. Bei neuen Node-Typen mit Bild immer diesen Flow prüfen.
|
- **`data.url` vs `storageId`:** Node-Komponenten erhalten `data.url` (aufgelöste HTTP-URL), nicht `storageId` direkt. Die URL wird von `convexNodeDocWithMergedStorageUrl` injiziert. Bei neuen Node-Typen mit Bild immer diesen Flow prüfen.
|
||||||
@@ -244,3 +279,4 @@ Im **Light Mode** wird der eigentliche Edge-`stroke` ebenfalls aus dieser Akzent
|
|||||||
- **Node-Taxonomie:** Alle Node-Typen sind in `lib/canvas-node-catalog.ts` definiert. Phase-2/3 Nodes haben `implemented: false` und `disabledHint`.
|
- **Node-Taxonomie:** Alle Node-Typen sind in `lib/canvas-node-catalog.ts` definiert. Phase-2/3 Nodes haben `implemented: false` und `disabledHint`.
|
||||||
- **Video-Connection-Policy:** `video-prompt` darf **nur** mit `ai-video` verbunden werden (und umgekehrt). `text → video-prompt` ist erlaubt (Prompt-Quelle). `ai-video → compare` ist erlaubt.
|
- **Video-Connection-Policy:** `video-prompt` darf **nur** mit `ai-video` verbunden werden (und umgekehrt). `text → video-prompt` ist erlaubt (Prompt-Quelle). `ai-video → compare` ist erlaubt.
|
||||||
- **Convex Generated Types:** `api.ai.generateVideo` wird u. U. nicht in `convex/_generated/api.d.ts` exportiert. Der Code verwendet `api as unknown as {...}` als Workaround. Ein `npx convex dev`-Zyklus würde die Typen korrekt generieren.
|
- **Convex Generated Types:** `api.ai.generateVideo` wird u. U. nicht in `convex/_generated/api.d.ts` exportiert. Der Code verwendet `api as unknown as {...}` als Workaround. Ein `npx convex dev`-Zyklus würde die Typen korrekt generieren.
|
||||||
|
- **Canvas Graph Query:** Der Canvas nutzt `canvasGraph.get` (aus `convex/canvasGraph.ts`) statt separater `nodes.list`/`edges.list` Queries. Optimistic Updates laufen über `canvas-graph-query-cache.ts`.
|
||||||
|
|||||||
@@ -10,93 +10,123 @@ UI-Komponenten für die Startseite nach dem Login.
|
|||||||
|-------|-------|
|
|-------|-------|
|
||||||
| `canvas-card.tsx` | Karte für einen Canvas in der Übersicht (Navigation, Umbenennen, Löschen mit Confirm-Dialog) |
|
| `canvas-card.tsx` | Karte für einen Canvas in der Übersicht (Navigation, Umbenennen, Löschen mit Confirm-Dialog) |
|
||||||
| `credit-overview.tsx` | Monatsverbrauch und verfügbare Credits als Balken-Visualisierung |
|
| `credit-overview.tsx` | Monatsverbrauch und verfügbare Credits als Balken-Visualisierung |
|
||||||
|
| `credits-activity-chart.tsx` | Credits-Aktivitäts-Chart (Recharts AreaChart) mit Verbrauch, Aktivität und verfügbaren Credits |
|
||||||
| `recent-transactions.tsx` | Liste der letzten Credit-Transaktionen |
|
| `recent-transactions.tsx` | Liste der letzten Credit-Transaktionen |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Layout-Seite
|
## Layout-Seite
|
||||||
|
|
||||||
`app/dashboard/page.tsx` — Server Component, rendert Dashboard-Layout mit den Komponenten oben.
|
`app/dashboard/page-client.tsx` — Client Component, rendert Dashboard mit gebündelter Snapshot-Query.
|
||||||
|
|
||||||
**Layout-Struktur:**
|
**Layout-Struktur:**
|
||||||
```
|
```
|
||||||
Dashboard
|
Dashboard
|
||||||
├── Header (User-Avatar, Name)
|
├── Header (Logo, Suche, User-Menü mit Theme-Wechsel)
|
||||||
├── Quick Actions (Create Canvas, Search)
|
├── Begrüßung (Name, Untertitel)
|
||||||
├── Credit Overview (Balance, Usage Bars)
|
├── Credit-Übersicht (Balance, Usage Bars)
|
||||||
├── Recent Transactions (List)
|
├── Arbeitsbereiche (Canvas Grid)
|
||||||
└── Canvas Grid (Canvas Cards)
|
├── Credits Verlauf + Transaktionen (zwei-spaltig)
|
||||||
|
│ ├── CreditsActivityChart (Recharts AreaChart)
|
||||||
|
│ └── Recent Transactions (List)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Dashboard Snapshot Architecture
|
||||||
|
|
||||||
|
**Früher:** Separate Convex-Queries für Balance, Subscription, UsageStats, Transactions, Canvases → 5+ gleichzeitige Queries.
|
||||||
|
|
||||||
|
**Jetzt:** Gebündelte Snapshot-Query (`api.dashboard.getSnapshot`) + localStorage-Cache.
|
||||||
|
|
||||||
|
### Datenfluss
|
||||||
|
|
||||||
|
```
|
||||||
|
page-client.tsx
|
||||||
|
└── useDashboardSnapshot(userId)
|
||||||
|
├── Convex Query: api.dashboard.getSnapshot (gebündelt)
|
||||||
|
│ → Balance + Subscription + UsageStats + Transactions + Canvases
|
||||||
|
├── localStorage Cache: readDashboardSnapshotCache (12h TTL)
|
||||||
|
│ → Sofortige Anzeige aus Cache während Query lädt
|
||||||
|
└── Automatisches Cache-Write bei neuen Query-Daten
|
||||||
|
```
|
||||||
|
|
||||||
|
### Snapshot Cache (`lib/dashboard-snapshot-cache.ts`)
|
||||||
|
|
||||||
|
- **Key:** `lemonspace.dashboard:snapshot:v1:<userId>`
|
||||||
|
- **TTL:** 12 Stunden (DEFAULT_TTL_MS)
|
||||||
|
- **Version:** `CACHE_VERSION = 1` — Version-Bumps invalidieren automatisch
|
||||||
|
- **Cache-Invalidierung:** Bei Logout wird der Cache des vorherigen Users gelöscht (via `sessionStorage("ls-last-dashboard-user")`)
|
||||||
|
- Defensiv: Alle Storage-Zugriffe sind try-catch-gewrappt (Quota-Fehler, Disabled Storage etc.)
|
||||||
|
|
||||||
|
### Snapshot Query (`convex/dashboard.ts`)
|
||||||
|
|
||||||
|
- `getSnapshot` — Gebündelte Query, lädt alle Dashboard-Daten in einem Convex-Call
|
||||||
|
- Nutzt `optionalAuth` → Default-Werte bei fehlender Session (kein Error)
|
||||||
|
- Transaktions-Priorisierung via `prioritizeRecentCreditTransactions` (Usage > Reservation > Refund > Topup > Subscription)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Datenquellen
|
## Datenquellen
|
||||||
|
|
||||||
Alle Daten kommen aus Convex-Queries via `useAuthQuery` (aus `hooks/use-auth-query.ts`):
|
| Komponente | Datenquelle |
|
||||||
|
|-----------|-------------|
|
||||||
|
| `credit-overview.tsx` | `dashboardSnapshot.balance`, `dashboardSnapshot.subscription`, `dashboardSnapshot.usageStats` |
|
||||||
|
| `credits-activity-chart.tsx` | `dashboardSnapshot.balance`, `dashboardSnapshot.recentTransactions` |
|
||||||
|
| `recent-transactions.tsx` | `dashboardSnapshot.recentTransactions` |
|
||||||
|
| `canvas-card.tsx` | `dashboardSnapshot.canvases` |
|
||||||
|
|
||||||
| Komponente | Query |
|
> Alle Daten kommen aus dem gebündelten Snapshot (`useDashboardSnapshot`). Keine separaten Queries mehr.
|
||||||
|-----------|-------|
|
|
||||||
| `canvas-card.tsx` | `api.canvases.list` |
|
|
||||||
| `credit-overview.tsx` | `api.credits.getBalance`, `api.credits.getUsageStats` |
|
|
||||||
| `recent-transactions.tsx` | `api.credits.getRecentTransactions` |
|
|
||||||
|
|
||||||
## Mutations
|
## Mutations
|
||||||
|
|
||||||
| Komponente | Mutation |
|
| Komponente | Mutation |
|
||||||
|-----------|----------|
|
|-----------|----------|
|
||||||
| `canvas-card.tsx` | `api.canvases.update`, `api.canvases.remove` |
|
| `canvas-card.tsx` | `api.canvases.update`, `api.canvases.remove` |
|
||||||
|
| `page-client.tsx` | `api.canvases.create` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Credits Activity Chart
|
||||||
|
|
||||||
|
Recharts-basiertes AreaChart (via ShadCN `ChartContainer`) mit drei Datenseries:
|
||||||
|
|
||||||
|
- **Usage** (gefüllt): Tatsächlicher Credit-Verbrauch pro Tag (committed usage-Transactions)
|
||||||
|
- **Activity** (gefüllt): Usage + aktive Reservationen pro Tag
|
||||||
|
- **Available** (Linie): Aktueller verfügbarer Credit-Stand als Referenzlinie
|
||||||
|
|
||||||
|
**Daten-Processing:** `lib/credits-activity.ts`
|
||||||
|
- `buildCreditsActivitySeries()` — Gruppiert Transactions nach Tag, aggregiert Usage/Activity
|
||||||
|
- `calculateUsageActivityDomain()` — Berechnet Y-Achsen-Domain mit Headroom
|
||||||
|
- `prioritizeRecentCreditTransactions()` — Sortiert Transactions nach Typ-Priorität
|
||||||
|
|
||||||
|
**Chart Config:** Teal für Usage, Accent-Foreground für Activity, Muted-Foreground für Available.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Konventionen
|
## Konventionen
|
||||||
|
|
||||||
- Kein lokaler State für Server-Daten — alles via Convex-Subscriptions (reaktiv)
|
- Kein lokaler State für Server-Daten — Snapshot kommt aus `useDashboardSnapshot`
|
||||||
- `useAuthQuery` statt `useQuery` verwenden, um Auth-Races zu vermeiden (skippt Query bis Token bereit ist)
|
- Dashboard zeigt sofort Cache-Daten an, während Live-Query lädt (`source: "cache" | "live" | "none"`)
|
||||||
- Canvas-Thumbnails sind optional (`thumbnail?: Id<"_storage">`) — Fallback-State immer behandeln
|
- Canvas-Thumbnails sind optional (`thumbnail?: Id<"_storage">`) — Fallback-State immer behandeln
|
||||||
- Responsive Grid-Layout für Canvas-Cards (`grid-cols-1 md:grid-cols-2 lg:grid-cols-3`)
|
- Responsive Grid-Layout für Canvas-Cards (`grid-cols-1 sm:grid-cols-3`)
|
||||||
- Leere Zustände (keine Canvases, keine Transaktionen) mit nutzerfreundlichen Platzhaltern zeigen
|
- Leere Zustände (keine Canvases, keine Transaktionen, keine Chart-Daten) mit nutzerfreundlichen Platzhaltern zeigen
|
||||||
|
- Chart-Komponente nutzt ShadCN `ChartContainer` (`components/ui/chart.tsx`) + Recharts 3.8
|
||||||
---
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Canvas Cards
|
|
||||||
|
|
||||||
Jede Karte zeigt:
|
|
||||||
- Canvas-Thumbnail (wenn verfügbar)
|
|
||||||
- Canvas-Name (truncate)
|
|
||||||
- Letzte Änderungszeit (relative Formatierung)
|
|
||||||
- Klick → Öffnen im Canvas-Editor
|
|
||||||
- Menü → Umbenennen, Löschen
|
|
||||||
|
|
||||||
### Credit Overview
|
|
||||||
|
|
||||||
Zeigt:
|
|
||||||
- Aktueller Credit-Balance (z.B. "350 Credits")
|
|
||||||
- Verbrauchs-Balken (Progress Bar) für aktuellen Monat
|
|
||||||
- Monats-Details (vom `api.credits.getUsageStats`)
|
|
||||||
- Link zum Upgrade im Abo-Settings
|
|
||||||
|
|
||||||
### Recent Transactions
|
|
||||||
|
|
||||||
Liste der letzten Credit-Transaktionen mit:
|
|
||||||
- Transaktions-Typ (Subscription, Top-Up, Usage)
|
|
||||||
- Betrag (+/-)
|
|
||||||
- Beschreibung
|
|
||||||
- Datum (relative Formatierung)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Error Handling
|
## Error Handling
|
||||||
|
|
||||||
- **Auth-Race**: `useAuthQuery` verhindert Fehler beim Initialisierungs-Flash
|
- **Auth-Race:** `useDashboardSnapshot` nutzt `useAuthQuery` intern → skippt bis Auth bereit
|
||||||
- **Empty States**: Wenn keine Canvases/Transaktionen vorhanden sind, informative Platzhalter zeigen
|
- **Empty States:** Wenn keine Canvases/Transaktionen vorhanden sind, informative Platzhalter zeigen
|
||||||
- **Offline**: Connection-Banner anzeigen, wenn Convex nicht erreichbar
|
- **Cache-Fehler:** localStorage-Zugriffe sind defensiv gewrappt (quota, disabled storage)
|
||||||
|
- **Offline:** Snapshot-Cache bietet Fallback bei fehlender Verbindung
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
- Canvas-Thumbnails werden über `api.canvases.list` geladen (nur wenn vorhanden)
|
- Gebündelte Snapshot-Query statt 5+ separater Queries → weniger WebSocket-Last
|
||||||
- Transaktions-Listen sind paginiert oder limitiert (z.B. letzte 20 Transaktionen)
|
- localStorage-Cache mit 12h TTL → sofortige Anzeige bei wiederholtem Besuch
|
||||||
- Kein lokaler State für Canvas-Daten — alles über real-time Convex-Subscriptions
|
- Canvas-Thumbnails werden über den Snapshot geladen (nur wenn vorhanden)
|
||||||
|
- Transaktions-Liste ist auf 20 Einträge limitiert (priorisiert nach Typ)
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ transition: all 200ms cubic-bezier(0.16, 1, 0.3, 1); /* expo-out */
|
|||||||
| `badge` | `components/ui/badge.tsx` | Status-Tags, Badges |
|
| `badge` | `components/ui/badge.tsx` | Status-Tags, Badges |
|
||||||
| `tooltip` | `components/ui/tooltip.tsx` | Explainer-Texte |
|
| `tooltip` | `components/ui/tooltip.tsx` | Explainer-Texte |
|
||||||
| `separator` | `components/ui/separator.tsx` | Trennungen |
|
| `separator` | `components/ui/separator.tsx` | Trennungen |
|
||||||
|
| `chart` | `components/ui/chart.tsx` | ShadCN Chart-Wrapper für Recharts (ChartContainer, ChartTooltip, ChartLegend) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -77,3 +78,18 @@ npx shadcn@latest add input
|
|||||||
```
|
```
|
||||||
|
|
||||||
Komponenten werden direkt in `components/ui/` abgelegt. Lokale Anpassungen erfolgen durch Überladen von Props oder direkt in der Komponentendatei.
|
Komponenten werden direkt in `components/ui/` abgelegt. Lokale Anpassungen erfolgen durch Überladen von Props oder direkt in der Komponentendatei.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Chart-Komponente (`chart.tsx`)
|
||||||
|
|
||||||
|
ShadCN-Wrapper für Recharts v3.8. Bietet konsistentes Styling für Chart-Elemente:
|
||||||
|
|
||||||
|
- `ChartContainer` — Root-Wrapper mit CSS-Variablen-basierten Farben
|
||||||
|
- `ChartTooltip` / `ChartTooltipContent` — Tooltip mit Design-Token-Support
|
||||||
|
- `ChartLegend` / `ChartLegendContent` — Legende mit konsistentem Styling
|
||||||
|
- `ChartConfig` — Typ für Chart-Farbkonfiguration
|
||||||
|
|
||||||
|
**Verwendung:** Dashboard Credits Activity Chart. Farben über CSS-Variablen (`hsl(var(--primary))` etc.).
|
||||||
|
|
||||||
|
**Abhängigkeit:** `recharts` v3.8.0 (in `package.json`).
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ Convex ist das vollständige Backend von LemonSpace: Datenbank, Realtime-Subscri
|
|||||||
| `storage.ts` | Convex File Storage Helpers + gebündelte Canvas-URL-Auflösung |
|
| `storage.ts` | Convex File Storage Helpers + gebündelte Canvas-URL-Auflösung |
|
||||||
| `export.ts` | Canvas-Export-Logik |
|
| `export.ts` | Canvas-Export-Logik |
|
||||||
| `http.ts` | HTTP-Endpunkte (Webhooks) |
|
| `http.ts` | HTTP-Endpunkte (Webhooks) |
|
||||||
|
| `dashboard.ts` | Gebündelte Dashboard-Snapshot-Query (Balance, Subscription, Usage, Transactions, Canvases in einem Call) |
|
||||||
|
| `canvasGraph.ts` | Canvas Graph Query — Performance-optimierte Query für Nodes+Edges in einem Call |
|
||||||
|
| `ai_errors.ts` | Error-Kategorisierung und User-facing Fehlermeldungen (aus `ai.ts` extrahiert) |
|
||||||
|
| `ai_node_data.ts` | Node-Data-Helpers (z. B. `getNodeDataRecord`) |
|
||||||
|
| `ai_retry.ts` | Retry-Logik für AI-Generierung (`generateImageWithAutoRetry`, aus `ai.ts` extrahiert) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -247,6 +252,7 @@ Wirft bei unauthentifiziertem Zugriff. Wird von allen Queries und Mutations genu
|
|||||||
- `credits.getSubscription` fällt bei fehlender Session auf Free/Active zurück (statt Throw), damit Tier-UI stabil bleibt.
|
- `credits.getSubscription` fällt bei fehlender Session auf Free/Active zurück (statt Throw), damit Tier-UI stabil bleibt.
|
||||||
- `credits.getRecentTransactions` gibt bei fehlender Session `[]` zurück (statt Throw), damit Aktivitätslisten beim Logout sauber leeren.
|
- `credits.getRecentTransactions` gibt bei fehlender Session `[]` zurück (statt Throw), damit Aktivitätslisten beim Logout sauber leeren.
|
||||||
- `credits.getUsageStats` gibt bei fehlender Session `0`-Statistiken zurück (statt Throw), damit Verbrauchsanzeigen ohne Fehler ausrendern.
|
- `credits.getUsageStats` gibt bei fehlender Session `0`-Statistiken zurück (statt Throw), damit Verbrauchsanzeigen ohne Fehler ausrendern.
|
||||||
|
- `dashboard.getSnapshot` gibt bei fehlender Session einen vollständigen Default-Snapshot zurück (Balance 0, Free-Tier, leere Listen)
|
||||||
|
|
||||||
### Idempotente Canvas-Mutations
|
### Idempotente Canvas-Mutations
|
||||||
|
|
||||||
@@ -305,6 +311,37 @@ Wirft bei unauthentifiziertem Zugriff. Wird von allen Queries und Mutations genu
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Dashboard Snapshot (`dashboard.ts`)
|
||||||
|
|
||||||
|
Gebündelte Query, die alle Dashboard-relevanten Daten in einem einzigen Convex-Call lädt. Ersetzt separate Queries für Balance, Subscription, UsageStats, Transactions und Canvases.
|
||||||
|
|
||||||
|
**`getSnapshot` (query, public):**
|
||||||
|
- Nutzt `optionalAuth` — gibt bei fehlender Session Default-Werte zurück (kein Throw)
|
||||||
|
- Lädt parallel: `creditBalances`, `subscriptions`, `creditTransactions` (usage-type), `creditTransactions` (recent, max 80), `canvases`
|
||||||
|
- Berechnet `monthlyUsage` und `totalGenerations` aus usage-Transactions des aktuellen Monats
|
||||||
|
- Nutzt `prioritizeRecentCreditTransactions` aus `lib/credits-activity.ts` für sortierte Transaktionsliste
|
||||||
|
- Gibt strukturiertes Snapshot-Objekt zurück: `{ balance, subscription, usageStats, recentTransactions, canvases, generatedAt }`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Canvas Graph Query (`canvasGraph.ts`)
|
||||||
|
|
||||||
|
Performance-optimierte Query, die Nodes und Edges für einen Canvas in einem einzigen Call lädt. Wird vom Frontend über den Canvas Graph Query Cache verwendet.
|
||||||
|
|
||||||
|
**`loadCanvasGraph(ctx, { canvasId, userId })` (internal):**
|
||||||
|
- Prüft Canvas-Existenz und Ownership
|
||||||
|
- Lädt Nodes und Edges parallel via `Promise.all`
|
||||||
|
- Performance-Logging bei Queries > 250ms
|
||||||
|
|
||||||
|
**`get` (query, public):**
|
||||||
|
- Nutzt `requireAuth`
|
||||||
|
- Delegiert an `loadCanvasGraph`
|
||||||
|
- Loggt bei langsamer Ausführung (`PERFORMANCE_LOG_THRESHOLD_MS = 250`)
|
||||||
|
|
||||||
|
**Frontend-Integration:** Der Canvas nutzt diese Query über `canvas-graph-query-cache.ts` (Optimistic Store Helper).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Konventionen
|
## Konventionen
|
||||||
|
|
||||||
- `internalMutation` / `internalAction` — Nur von anderen Convex-Funktionen aufrufbar, nicht direkt vom Client.
|
- `internalMutation` / `internalAction` — Nur von anderen Convex-Funktionen aufrufbar, nicht direkt vom Client.
|
||||||
|
|||||||
@@ -87,6 +87,30 @@ const { isOnline, pendingOps, syncStatus } = useCanvasSyncStatus()
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### `use-dashboard-snapshot.ts`
|
||||||
|
|
||||||
|
Hook für gebündeltes Dashboard-Datenladen mit localStorage-Cache. Ersetzt separate Queries für Balance, Subscription, UsageStats, Transactions und Canvases.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Gebündelte Convex-Query (`api.dashboard.getSnapshot`) in einem Call
|
||||||
|
- Automatisches Caching im localStorage (12h TTL)
|
||||||
|
- Sofortige Anzeige aus Cache während Live-Query lädt
|
||||||
|
- Cache-Invalidierung bei Logout (via sessionStorage Tracking)
|
||||||
|
- Source-Tracking: `{ snapshot, source: "live" | "cache" | "none" }`
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
```typescript
|
||||||
|
const { snapshot, source } = useDashboardSnapshot(userId)
|
||||||
|
// source="cache" → sofortige Anzeige aus Cache
|
||||||
|
// source="live" → aktuelle Daten vom Server
|
||||||
|
// source="none" → weder Cache noch Query vorhanden
|
||||||
|
```
|
||||||
|
|
||||||
|
**Typen:**
|
||||||
|
- `DashboardSnapshot` — Vollständiger Rückgabewert von `api.dashboard.getSnapshot`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Konventionen
|
## Konventionen
|
||||||
|
|
||||||
- Hooks immer mit `use-` Prefix im Dateinamen
|
- Hooks immer mit `use-` Prefix im Dateinamen
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ Geteilte Hilfsfunktionen, Typ-Definitionen und Konfiguration. Keine React-Kompon
|
|||||||
| `topup-calculator.ts` | Bonus-Staffel-Berechnung für Credit-Top-Ups |
|
| `topup-calculator.ts` | Bonus-Staffel-Berechnung für Credit-Top-Ups |
|
||||||
| `format-time.ts` | Zeitformatierung (relative Zeitangaben) |
|
| `format-time.ts` | Zeitformatierung (relative Zeitangaben) |
|
||||||
| `utils.ts` | `cn()` (clsx + tailwind-merge), allgemeine Utilities |
|
| `utils.ts` | `cn()` (clsx + tailwind-merge), allgemeine Utilities |
|
||||||
|
| `credits-activity.ts` | Credits-Aktivitäts-Analytics: Transaktions-Priorisierung, Activity-Series, Usage-Domain-Berechnung |
|
||||||
|
| `dashboard-snapshot-cache.ts` | localStorage-Cache für Dashboard-Snapshots (12h TTL, versioniert) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -198,6 +200,51 @@ Verwendet in `convex/ai.ts` (`pollVideoTask`) und `convex/freepik.ts` (`getVideo
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## `credits-activity.ts` — Credits Analytics
|
||||||
|
|
||||||
|
Analytics-Helpers für die Dashboard Credits-Aktivitäts-Anzeige. Wird von `components/dashboard/credits-activity-chart.tsx` und `convex/dashboard.ts` verwendet.
|
||||||
|
|
||||||
|
**Kernfunktionen:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Transaktions-Priorisierung (Usage → Reservation → Refund → Topup → Subscription)
|
||||||
|
prioritizeRecentCreditTransactions(transactions, limit)
|
||||||
|
|
||||||
|
// Tages-aggregierte Activity-Series für Recharts (max. 7 Tage)
|
||||||
|
buildCreditsActivitySeries(transactions, availableCredits, locale, maxPoints?)
|
||||||
|
|
||||||
|
// Y-Achsen-Domain mit Headroom (mind. 8, +20% Headroom)
|
||||||
|
calculateUsageActivityDomain(points)
|
||||||
|
|
||||||
|
// Credit-Formatierung ("1.234 Cr")
|
||||||
|
formatCredits(value, locale)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Typen:**
|
||||||
|
- `CreditTransactionLike` — Minimales Transaction-Interface für Analytics
|
||||||
|
- `CreditActivityPoint` — Tages-Datenpunkt (`{ day, usage, activity, available }`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `dashboard-snapshot-cache.ts` — Dashboard Snapshot Cache
|
||||||
|
|
||||||
|
localStorage-basierter Cache für Dashboard-Snapshot-Daten.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
readDashboardSnapshotCache<T>(userId, options?) // Cached Snapshot lesen (mit TTL-Prüfung)
|
||||||
|
writeDashboardSnapshotCache<T>(userId, snapshot) // Snapshot schreiben
|
||||||
|
clearDashboardSnapshotCache(userId) // Cache invalidieren
|
||||||
|
```
|
||||||
|
|
||||||
|
**Konfiguration:**
|
||||||
|
- Namespace: `lemonspace.dashboard`
|
||||||
|
- Key: `lemonspace.dashboard:snapshot:v1:<userId>`
|
||||||
|
- TTL: 12 Stunden (`DEFAULT_TTL_MS = 12 * 60 * 60 * 1000`)
|
||||||
|
- Version: `CACHE_VERSION = 1` — Bumps invalidieren bestehende Caches
|
||||||
|
- Alle Storage-Zugriffe defensiv (try-catch, quota handling)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## `canvas-local-persistence.ts` — localStorage-Cache
|
## `canvas-local-persistence.ts` — localStorage-Cache
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
|||||||
Reference in New Issue
Block a user