docs: add Sub-Dokumentation section to AGENTS.md and CLAUDE.md for improved documentation structure
- Introduced a new section detailing the Single Source of Truth for various components, linking to their respective CLAUDE.md files. - Enhanced clarity on documentation organization across different areas of the project.
This commit is contained in:
137
components/canvas/CLAUDE.md
Normal file
137
components/canvas/CLAUDE.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# components/canvas/ — Canvas-Engine
|
||||
|
||||
Der Canvas ist das Herzstück von LemonSpace. Er basiert auf `@xyflow/react` (React Flow) und synchronisiert seinen Zustand bidirektional mit Convex.
|
||||
|
||||
---
|
||||
|
||||
## Architektur
|
||||
|
||||
```
|
||||
app/(app)/canvas/[canvasId]/page.tsx
|
||||
└── <Canvas canvasId={...} /> ← components/canvas/canvas.tsx
|
||||
├── <ReactFlowProvider>
|
||||
│ └── <CanvasInner> ← Haupt-Komponente (2800 Zeilen)
|
||||
│ ├── Convex useQuery ← Realtime-Sync
|
||||
│ ├── nodeTypes Map ← node-types.ts
|
||||
│ ├── localStorage Cache ← canvas-local-persistence.ts
|
||||
│ └── Panel-Komponenten
|
||||
└── Context Providers
|
||||
```
|
||||
|
||||
**`canvas.tsx`** ist die zentrale Datei. Sie enthält die gesamte State-Management-Logik, Convex-Mutations, Optimistic Updates und Event-Handler. Sehr groß — vor Änderungen immer den genauen Abschnitt lesen.
|
||||
|
||||
---
|
||||
|
||||
## Convex ↔ React Flow Mapping
|
||||
|
||||
Convex und React Flow verwenden unterschiedliche Datenmodelle. Das Mapping liegt in `lib/canvas-utils.ts`:
|
||||
|
||||
| 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)` |
|
||||
|
||||
**Wichtig:** Convex speichert `positionX` / `positionY` als separate Felder. React Flow erwartet `position: { x, y }`. Niemals RF-Node-Objekte direkt in Convex schreiben.
|
||||
|
||||
**Status-Injection:** `convexNodeToRF` schreibt `_status`, `_statusMessage` und `retryCount` in `data`, damit Node-Komponenten darauf zugreifen können ohne das Node-Dokument direkt zu kennen.
|
||||
|
||||
**URL-Caching:** Images mit `storageId` werden über einen batch-Storage-URL-Query aufgelöst (`urlByStorage`-Map). Die vorherige URL wird in `previousDataByNodeId` gecacht, um Flackern beim Reload zu vermeiden.
|
||||
|
||||
---
|
||||
|
||||
## 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`)
|
||||
|
||||
```
|
||||
image: 280 × 200 prompt: 288 × 220
|
||||
text: 256 × 120 ai-image: 320 × 408
|
||||
group: 400 × 300 frame: 400 × 300
|
||||
note: 208 × 100 compare: 500 × 380
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Node-Status-Modell
|
||||
|
||||
```
|
||||
idle → analyzing → clarifying → executing (retry N/2) → done
|
||||
→ error
|
||||
```
|
||||
|
||||
Status + `statusMessage` werden direkt am Node angezeigt. Kein globales Loading-Banner. Bei `error` zeigt `statusMessage` die Kategorie: `Credits: ...`, `Timeout: ...`, `Provider: ...` etc.
|
||||
|
||||
---
|
||||
|
||||
## Edge-Glow-System
|
||||
|
||||
Jede Edge bekommt einen `drop-shadow`-Filter entsprechend dem Quell-Node-Typ. Farben in `lib/canvas-utils.ts → SOURCE_NODE_GLOW_RGB`:
|
||||
|
||||
- `prompt`, `ai-image` → Violett (139, 92, 246)
|
||||
- `image`, `text`, `note` → Teal (13, 148, 136)
|
||||
- `frame` → Orange (249, 115, 22)
|
||||
- `group`, `compare` → Grau (100, 116, 139)
|
||||
|
||||
Compare-Node hat zusätzlich Handle-spezifische Farben (`left` → Blau, `right` → Smaragd).
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
**localStorage-Cache** (`lib/canvas-local-persistence.ts`): Snapshot + Ops-Queue pro Canvas-ID.
|
||||
|
||||
- Key-Schema: `lemonspace.canvas:snapshot:v1:<canvasId>` und `lemonspace.canvas:ops:v1:<canvasId>`
|
||||
- Snapshot = letzter bekannter State (Nodes + Edges) für schnellen initialen Render
|
||||
- Ops-Queue = ausstehende Mutations (Recovery bei Verbindungsabbruch, Konzept — noch nicht vollständig implementiert)
|
||||
|
||||
---
|
||||
|
||||
## Panel-Komponenten
|
||||
|
||||
| Datei | Zweck |
|
||||
|-------|-------|
|
||||
| `canvas-toolbar.tsx` | Werkzeug-Leiste (Select, Pan, Zoom-Controls) |
|
||||
| `canvas-app-menu.tsx` | App-Menü (Einstellungen, Logout, Canvas-Name) |
|
||||
| `canvas-sidebar.tsx` | Node-Palette (linke Seite) |
|
||||
| `canvas-command-palette.tsx` | Cmd+K Command Palette |
|
||||
| `canvas-connection-drop-menu.tsx` | Kontext-Menü beim Loslassen einer Verbindung |
|
||||
| `canvas-node-template-picker.tsx` | Node aus Template einfügen |
|
||||
| `canvas-placement-context.tsx` | Context für Drag-and-Drop-Platzierung |
|
||||
| `asset-browser-panel.tsx` | Freepik/Stock-Asset-Browser |
|
||||
| `video-browser-panel.tsx` | Video-Asset-Browser |
|
||||
| `canvas-user-menu.tsx` | User-Avatar und Menü |
|
||||
| `credit-display.tsx` | Credit-Balance Anzeige in der Toolbar |
|
||||
| `export-button.tsx` | Export-Button mit Format-Auswahl |
|
||||
| `connection-banner.tsx` | Offline-Banner bei Convex-Verbindungsverlust |
|
||||
| `custom-connection-line.tsx` | Angepasste temporäre Verbindungslinie |
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
- **Min-Zoom:** `CANVAS_MIN_ZOOM = 0.5 / 3` — dreimal weiter raus als React-Flow-Default.
|
||||
- **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`.
|
||||
Reference in New Issue
Block a user