feat(docs): update LemonSpace manifest and PRD for v2.0 release

- Updated version from v1.5 to v2.0 in both the LemonSpace Manifest and PRD documents.
- Expanded Phase 1 scope to include video and asset nodes, and integrated non-destructive image editing capabilities.
- Enhanced node taxonomy to reflect 6 categories with 27 node types.
- Added details on offline sync features and optimistic updates in the documentation.
- Improved clarity and structure of the product vision and problem statement sections.
This commit is contained in:
2026-04-06 22:27:21 +02:00
parent 36e8b7d3db
commit 456b910532
10 changed files with 744 additions and 219 deletions

View File

@@ -48,3 +48,41 @@ Die Credit-Balance wird auch in `components/dashboard/credit-overview.tsx` angez
- Tier-Upgrades immer über Polar-Checkout (kein direktes Schreiben in `subscriptions`-Tabelle)
- `topUp` Mutation (`convex/credits.ts`) für Credit-Nachkauf aufrufen
- Monatliches Top-Up-Limit pro Tier beachten (siehe `TIER_CONFIG.topUpLimit`)
---
## Features
### Pricing Cards
Zeigt alle verfügbaren Tiers mit:
- Beschreibung
- Credits pro Monat
- Preise (Euro)
- Feature-Checkmarks
- Aktives Tier hervorheben
### Manage Subscription
UI für:
- Aktuelles Tier anzeigen
- Upgrade zum nächsthöheren Tier
- Abonnement verwalten (Stornierung, Abo verwalten)
- Webhook-Feedback-Feedback
### Top-Up Panel
Bietet:
- Feste Pakete (z.B. 100 Credits, 500 Credits)
- Custom-Betrag
- Bonus-Staffel-Berechnung (`topup-calculator.ts`)
- Direkter Checkout über Polar
---
## Best Practices
1. **UI-Updates**: Änderungen an Tiers/Kosten immer in `convex/credits.ts` zuerst ändern, dann in UI-Dateien anpassen
2. **Mock-Mode**: Für Development kann `ALLOW_TEST_CREDIT_GRANT=true` verwendet werden, um Test-Credits zu generieren
3. **Error Handling**: Webhook-Fehler und Payment-Fehler müssen im Dashboard transparent dargestellt werden
4. **Audit Trail**: Alle Credit-Transaktionen (subscriptions, topups, usage) müssen im Backend persistiert werden

View File

@@ -39,47 +39,52 @@ app/(app)/canvas/[canvasId]/page.tsx
---
## Convex ↔ React Flow Mapping
## Node-Taxonomie (Phase 1)
Convex und React Flow verwenden unterschiedliche Datenmodelle. Das Mapping liegt in `lib/canvas-utils.ts`:
Alle verfügbaren Node-Typen sind in `lib/canvas-node-catalog.ts` definiert:
| Richtung | Funktion |
|----------|----------|
| Convex Node → RF Node | `convexNodeToRF(doc)` |
| Convex Edge → RF Edge | `convexEdgeToRF(doc)` |
| Edge + Glow | `convexEdgeToRFWithSourceGlow(edge, sourceType, colorMode)` |
| StorageId → URL merge | `convexNodeDocWithMergedStorageUrl(node, urlMap, prevMap)` |
### Kategorien
**Wichtig:** Convex speichert `positionX` / `positionY` als separate Felder. React Flow erwartet `position: { x, y }`. Niemals RF-Node-Objekte direkt in Convex schreiben.
| Kategorie | Nodes | Beschreibung |
|-----------|-------|-------------|
| **source** (Quelle) | `image`, `text`, `video`, `asset`, `color` | Input-Quellen für den Workflow |
| **ai-output** (KI-Ausgabe) | `prompt`, `ai-text`, `ai-video`, `agent-output` | KI-generierte Inhalte |
| **transform** (Transformation) | `crop`, `bg-remove`, `upscale` | Bildbearbeitung-Transformationen |
| **image-edit** (Bildbearbeitung) | `curves`, `color-adjust`, `light-adjust`, `detail-adjust` | Preset-basierte Adjustments |
| **control** (Steuerung & Flow) | `condition`, `loop`, `parallel`, `switch` | Kontrollfluss-Elemente |
| **layout** (Canvas & Layout) | `group`, `frame`, `note`, `compare` | Layout-Elemente |
**Status-Injection:** `convexNodeToRF` schreibt `_status`, `_statusMessage` und `retryCount` in `data`, damit Node-Komponenten darauf zugreifen können ohne das Node-Dokument direkt zu kennen.
### Node-Typen im Detail
**URL-Caching:** Images mit `storageId` werden im Canvas nicht mehr über eine reaktive Query aufgelöst. `canvas.tsx` sammelt die aktuellen `storageId`s aus `nodes.list` und ruft `storage.batchGetUrlsForCanvas` gezielt per Mutation auf, nur wenn sich das Set ändert. Die vorherige URL wird in `previousDataByNodeId` gecacht, um Flackern beim Reload zu vermeiden.
| Typ | Phase | Implementiert | Kategorie | Handles |
|-----|-------|---------------|-----------|---------|
| `image` | 1 | ✅ | source | source (default), target (default) |
| `text` | 1 | ✅ | source | source (default), target (default) |
| `video` | 1 | ✅ | source | source (default), target (default) |
| `asset` | 1 | ✅ | source | source (default), target (default) |
| `prompt` | 1 | ✅ | ai-output | source: `prompt-out`, target: `image-in` |
| `ai-text` | 2 | 🔲 | ai-output | source: `text-out`, target: `text-in` |
| `ai-video` | 2 | 🔲 | ai-output | source: `video-out`, target: `video-in` |
| `agent-output` | 3 | 🔲 | ai-output | systemOutput: true |
| `crop` | 2 | 🔲 | transform | 🔲 |
| `bg-remove` | 2 | 🔲 | transform | 🔲 |
| `upscale` | 2 | 🔲 | transform | 🔲 |
| `curves` | 1 | ✅ | image-edit | Preset-basiert (nicht standalone) |
| `color-adjust` | 1 | ✅ | image-edit | Preset-basiert |
| `light-adjust` | 1 | ✅ | image-edit | Preset-basiert |
| `detail-adjust` | 1 | ✅ | image-edit | Preset-basiert |
| `group` | 1 | ✅ | layout | source (default), target (default) |
| `frame` | 1 | ✅ | layout | source: `frame-out`, target: `frame-in` |
| `note` | 1 | ✅ | layout | source (default), target (default) |
| `compare` | 1 | ✅ | layout | source: `compare-out`, targets: `left`, `right` |
**Load-Shedding-Hot-Path:** Der Canvas-Hot-Path soll so wenig Convex-Abhängigkeiten wie möglich haben. Direkt reaktiv bleiben nur die Kernmodelle (`nodes.list`, `edges.list`, `canvases.get`). Nebenpfade wie Storage-URL-Auflösung, Adjustment-Presets und Toolbar-Credits sind bewusst entkoppelt oder zusammengefasst.
> `implemented: false` (🔲) bedeutet Phase-2/3 Node, der noch nicht implementiert ist. **Hinweis:** Phase-2/3 Nodes müssen im Schema (`convex/node_type_validator.ts`) vordeklariert werden, damit das System nicht bei jeder Phasenübergang neu migriert werden muss. Die UI filtert Nodes nach Phase.
**SystemOutput Nodes** (`ai-text`, `ai-video`, `agent-output`): Wird typischerweise vom KI-System erzeugt — nicht aus Palette/DnD anlegbar.
---
## Node-Typen (Phase 1)
Alle registrierten Node-Typen in `node-types.ts`:
| Typ | Komponente | Kategorie | Handles |
|-----|-----------|-----------|---------|
| `image` | `ImageNode` | Quelle | source (default), target (default) |
| `text` | `TextNode` | Quelle | source (default), target (default) |
| `prompt` | `PromptNode` | KI-Ausgabe | source: `prompt-out`, target: `image-in` |
| `ai-image` | `AiImageNode` | KI-Ausgabe | source: `image-out`, target: `prompt-in` |
| `group` | `GroupNode` | Layout | source (default), target (default) |
| `frame` | `FrameNode` | Layout | source: `frame-out`, target: `frame-in` |
| `note` | `NoteNode` | Layout | source (default), target (default) |
| `compare` | `CompareNode` | Layout | source: `compare-out`, targets: `left`, `right` |
| `asset` | `AssetNode` | Quelle | source (default), target (default) |
| `video` | `VideoNode` | Quelle | source (default), target (default) |
> `nodeTypes`-Map **muss** außerhalb jeder React-Komponente definiert sein (sonst re-rendert React Flow bei jedem Render alle Nodes).
### Default-Größen (`lib/canvas-utils.ts → NODE_DEFAULTS`)
## Default-Größen (`lib/canvas-utils.ts → NODE_DEFAULTS`)
```
image: 280 × 200 prompt: 288 × 220
@@ -94,7 +99,7 @@ note: 208 × 100 compare: 500 × 380
```
idle → analyzing → clarifying → executing (retry N/2) → done
→ error
→ error
```
Status + `statusMessage` werden direkt am Node angezeigt. Kein globales Loading-Banner. Bei `error` zeigt `statusMessage` die Kategorie: `Credits: ...`, `Timeout: ...`, `Provider: ...` etc.
@@ -109,6 +114,7 @@ Jede Edge bekommt einen `drop-shadow`-Filter entsprechend dem Quell-Node-Typ. Fa
- `image`, `text`, `note` → Teal (13, 148, 136)
- `frame` → Orange (249, 115, 22)
- `group`, `compare` → Grau (100, 116, 139)
- `curves`, `color-adjust`, `light-adjust`, `detail-adjust` → Pink (236, 72, 153)
Compare-Node hat zusätzlich Handle-spezifische Farben (`left` → Blau, `right` → Smaragd).
@@ -116,6 +122,20 @@ Im **Light Mode** wird der eigentliche Edge-`stroke` ebenfalls aus dieser Akzent
---
## Adjustments (Curves, Color, Light, Detail)
**Wichtig:** Diese Nodes werden **nicht als eigene Node-Typen** in der Palette angezeigt. Stattdessen existieren sie als **Presets**, die direkt in einem vorhandenen Node angewendet werden können.
- Preset-Verwaltung: `presets.list` (Convex-Query)
- Preset-Provider: `CanvasPresetsProvider` (Kontext)
- Hook: `useCanvasAdjustmentPresets()` für Preset-Management
**Regeln:**
- Curves-, Color-, Light-, Detail-Adjustment Nodes dürfen keine eigene `presets.list`-Query feuern.
- Immer `CanvasPresetsProvider` + `useCanvasAdjustmentPresets(...)` verwenden.
---
## Optimistic Updates & Local Persistence
**Optimistic Prefix:** Temporäre Nodes/Edges erhalten IDs mit `optimistic_` / `optimistic_edge_`-Prefix. Sie werden durch echte Convex-IDs ersetzt sobald die Mutation abgeschlossen ist.
@@ -153,6 +173,10 @@ Im **Light Mode** wird der eigentliche Edge-`stroke` ebenfalls aus dieser Akzent
| `export-button.tsx` | Export-Button mit Format-Auswahl |
| `connection-banner.tsx` | Offline-Banner bei Convex-Verbindungsverlust |
| `custom-connection-line.tsx` | Angepasste temporäre Verbindungslinie |
| `default-edge.tsx` | Standard Edge-Rendering |
| `node-error-boundary.tsx` | Error-Boundary für Node-Fehler |
| `adjustment-preview.tsx` | Vorschau für Adjustment-Presets |
| `adjustment-controls.tsx` | UI-Controls für Adjustments |
---
@@ -181,3 +205,5 @@ Im **Light Mode** wird der eigentliche Edge-`stroke` ebenfalls aus dieser Akzent
- **Parent-Nodes:** `parentId` zeigt auf einen Group- oder Frame-Node. React Flow erwartet, dass Parent-Nodes vor Child-Nodes in der `nodes`-Array stehen.
- **Bridge-Edges:** Beim Löschen eines mittleren Nodes werden Kanten automatisch neu verbunden (`computeBridgeCreatesForDeletedNodes` aus `lib/canvas-utils.ts`).
- **`"null"`-Handles:** Convex kann `"null"` als String speichern. `convexEdgeToRF` sanitized Handles: `"null"``undefined`.
- **Optimistic IDs:** Temporäre Nodes/Edges erhalten IDs mit `optimistic_` / `optimistic_edge_`-Prefix, werden durch echte Convex-IDs ersetzt, sobald die Mutation abgeschlossen ist.
- **Node-Taxonomie:** Alle Node-Typen sind in `lib/canvas-node-catalog.ts` definiert. Phase-2/3 Nodes haben `implemented: false` und `disabledHint`.

View File

@@ -14,6 +14,22 @@ UI-Komponenten für die Startseite nach dem Login.
---
## Layout-Seite
`app/dashboard/page.tsx` — Server Component, rendert Dashboard-Layout mit den Komponenten oben.
**Layout-Struktur:**
```
Dashboard
├── Header (User-Avatar, Name)
├── Quick Actions (Create Canvas, Search)
├── Credit Overview (Balance, Usage Bars)
├── Recent Transactions (List)
└── Canvas Grid (Canvas Cards)
```
---
## Datenquellen
Alle Daten kommen aus Convex-Queries via `useAuthQuery` (aus `hooks/use-auth-query.ts`):
@@ -32,14 +48,55 @@ Alle Daten kommen aus Convex-Queries via `useAuthQuery` (aus `hooks/use-auth-que
---
## Layout-Seite
`app/dashboard/page.tsx` — Server Component, rendert Dashboard-Layout mit den Komponenten oben.
---
## Konventionen
- Kein lokaler State für Server-Daten — alles via Convex-Subscriptions (reaktiv)
- `useAuthQuery` statt `useQuery` verwenden, um Auth-Races zu vermeiden (skippt Query bis Token bereit ist)
- 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`)
- Leere Zustände (keine Canvases, keine Transaktionen) mit nutzerfreundlichen Platzhaltern zeigen
---
## 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
- **Auth-Race**: `useAuthQuery` verhindert Fehler beim Initialisierungs-Flash
- **Empty States**: Wenn keine Canvases/Transaktionen vorhanden sind, informative Platzhalter zeigen
- **Offline**: Connection-Banner anzeigen, wenn Convex nicht erreichbar
---
## Performance
- Canvas-Thumbnails werden über `api.canvases.list` geladen (nur wenn vorhanden)
- Transaktions-Listen sind paginiert oder limitiert (z.B. letzte 20 Transaktionen)
- Kein lokaler State für Canvas-Daten — alles über real-time Convex-Subscriptions

View File

@@ -49,3 +49,31 @@ transition: all 200ms cubic-bezier(0.16, 1, 0.3, 1); /* expo-out */
2. In `components/ui/` ablegen (automatisch durch ShadCN-CLI)
3. Anpassungen direkt in der Komponente — keine separaten Override-Dateien
4. Token-Konventionen aus `globals.css` verwenden, keine Hardcoded-Farben
---
## Layout-Komponenten
| Komponente | Pfad | Verwendung |
|------------|------|------------|
| `resizable` | `components/ui/resizable.tsx` | Sidebar-Resizing in Canvas |
| `dialog` | `components/ui/dialog.tsx` | Confirmations, Popovers |
| `popover` | `components/ui/popover.tsx` | Dropdowns, Kontext-Menüs |
| `badge` | `components/ui/badge.tsx` | Status-Tags, Badges |
| `tooltip` | `components/ui/tooltip.tsx` | Explainer-Texte |
| `separator` | `components/ui/separator.tsx` | Trennungen |
---
## ShadCN Integration
ShadCN-Komponenten werden nicht als npm-Paket installiert, sondern als Copy-Paste in das Projekt kopiert. Dies ermöglicht volle Kontrolle über die Implementierung und Anpassung an die LemonSpace-Design-Systeme.
**Standard-Prozess:**
```bash
npx shadcn@latest add button
npx shadcn@latest add input
# ... andere Komponenten
```
Komponenten werden direkt in `components/ui/` abgelegt. Lokale Anpassungen erfolgen durch Überladen von Props oder direkt in der Komponentendatei.