feat(canvas, convex, lib): integrate AI video generation capabilities and enhance documentation
- Expanded the AI pipeline to support video generation, including the addition of `video-prompt` and `ai-video` node types. - Updated connection policies to validate interactions between new video nodes and existing node types. - Enhanced logging and error handling for video generation processes, including detailed polling strategies and response formats. - Updated documentation to reflect new node types, their functionalities, and integration details within the canvas and backend systems. - Ensured synchronization between client-side and backend model definitions for both image and video generation.
This commit is contained in:
@@ -31,7 +31,7 @@ app/(app)/canvas/[canvasId]/page.tsx
|
||||
| `canvas-helpers.ts` | Shared Utility-Layer (Optimistic IDs, Node-Merge, Compare-Resolution, Edge/Hit-Helpers, Konstante Defaults) |
|
||||
| `canvas-presets-context.tsx` | Shared Preset-Provider für Adjustment-Nodes; bündelt `presets.list` zu einer einzigen Query |
|
||||
| `canvas-node-change-helpers.ts` | Dimensions-/Resize-Transformationen für `asset` und `ai-image` Nodes |
|
||||
| `canvas-generation-failures.ts` | Hook für AI-Generation-Error-Tracking mit Schwellenwert-Toast |
|
||||
| `canvas-generation-failures.ts` | Hook für AI-Generation-Error-Tracking mit Schwellenwert-Toast (unterstützt `ai-image` und `ai-video`) |
|
||||
| `canvas-scissors.ts` | Hook für Scherenmodus (K/Esc Toggle, Click-Cut, Stroke-Cut) |
|
||||
| `canvas-delete-handlers.ts` | Hook für `onBeforeDelete`, `onNodesDelete`, `onEdgesDelete` inkl. Bridge-Edges |
|
||||
| `canvas-reconnect.ts` | Hook für Edge-Reconnect (`onReconnectStart`, `onReconnect`, `onReconnectEnd`) |
|
||||
@@ -48,7 +48,7 @@ Alle verfügbaren Node-Typen sind in `lib/canvas-node-catalog.ts` definiert:
|
||||
| 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 |
|
||||
| **ai-output** (KI-Ausgabe) | `prompt`, `video-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 |
|
||||
@@ -63,8 +63,9 @@ Alle verfügbaren Node-Typen sind in `lib/canvas-node-catalog.ts` definiert:
|
||||
| `video` | 1 | ✅ | source | source (default), target (default) |
|
||||
| `asset` | 1 | ✅ | source | source (default), target (default) |
|
||||
| `prompt` | 1 | ✅ | ai-output | source: `prompt-out`, target: `image-in` |
|
||||
| `video-prompt` | 2 | ✅ | ai-output | source: `video-prompt-out`, target: `video-prompt-in` |
|
||||
| `ai-text` | 2 | 🔲 | ai-output | source: `text-out`, target: `text-in` |
|
||||
| `ai-video` | 2 | 🔲 | ai-output | source: `video-out`, target: `video-in` |
|
||||
| `ai-video` | 2 | ✅ (systemOutput) | ai-output | source: `video-out`, target: `video-in` |
|
||||
| `agent-output` | 3 | 🔲 | ai-output | systemOutput: true |
|
||||
| `crop` | 2 | 🔲 | transform | 🔲 |
|
||||
| `bg-remove` | 2 | 🔲 | transform | 🔲 |
|
||||
@@ -80,7 +81,29 @@ Alle verfügbaren Node-Typen sind in `lib/canvas-node-catalog.ts` definiert:
|
||||
|
||||
> `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.
|
||||
**SystemOutput Nodes** (`ai-video`, `ai-text`, `agent-output`): Wird typischerweise vom KI-System erzeugt — nicht aus Palette/DnD anlegbar. `ai-video` wird automatisch durch `createNodeConnectedFromSource` beim Klick auf "Video generieren" erzeugt.
|
||||
|
||||
---
|
||||
|
||||
## KI-Video-Node-Flow
|
||||
|
||||
Zweistufiger Node-Flow analog `prompt → ai-image`:
|
||||
|
||||
1. **`video-prompt`** (Steuernode, Palette-sichtbar): Prompt-Textarea, Modell-Selector, Dauer-Selector (5s/10s), Credit-Kosten-Anzeige, "Video generieren"-Button
|
||||
2. **`ai-video`** (Output-Node, systemOutput): Wird automatisch rechts vom video-prompt erzeugt. Zeigt Status (executing/done/error), fertiges Video als `<video>`-Player, Metadaten und Retry-Button.
|
||||
|
||||
**Frontend-Flow:**
|
||||
1. User fügt `video-prompt` aus Sidebar/Command Palette ein
|
||||
2. User gibt Prompt ein, wählt Modell + Dauer
|
||||
3. Klick auf "Video generieren" → `createNodeConnectedFromSource` erzeugt `ai-video`-Node
|
||||
4. `useAction(api.ai.generateVideo)` startet Backend-Job
|
||||
5. Node zeigt `executing`-Status mit Shimmer
|
||||
6. Bei `done`: Video aus Convex Storage wird abgespielt
|
||||
7. Bei `error`: Retry-Button → findet verbundenen `video-prompt`-Source → `generateVideo` erneut
|
||||
|
||||
**Node-Komponenten:**
|
||||
- `components/canvas/nodes/video-prompt-node.tsx` — Steuernode mit Generate-Button
|
||||
- `components/canvas/nodes/ai-video-node.tsx` — Output-Node mit Player, Metadaten, Retry
|
||||
|
||||
---
|
||||
|
||||
@@ -89,6 +112,7 @@ Alle verfügbaren Node-Typen sind in `lib/canvas-node-catalog.ts` definiert:
|
||||
```
|
||||
image: 280 × 200 prompt: 288 × 220
|
||||
text: 256 × 120 ai-image: 320 × 408
|
||||
video-prompt: 288 × 220 ai-video: 360 × 280
|
||||
group: 400 × 300 frame: 400 × 300
|
||||
note: 208 × 100 compare: 500 × 380
|
||||
```
|
||||
@@ -111,6 +135,7 @@ Status + `statusMessage` werden direkt am Node angezeigt. Kein globales Loading-
|
||||
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)
|
||||
- `video-prompt`, `ai-video` → Violett (124, 58, 237)
|
||||
- `image`, `text`, `note` → Teal (13, 148, 136)
|
||||
- `frame` → Orange (249, 115, 22)
|
||||
- `group`, `compare` → Grau (100, 116, 139)
|
||||
@@ -178,6 +203,15 @@ Im **Light Mode** wird der eigentliche Edge-`stroke` ebenfalls aus dieser Akzent
|
||||
| `adjustment-preview.tsx` | Vorschau für Adjustment-Presets |
|
||||
| `adjustment-controls.tsx` | UI-Controls für Adjustments |
|
||||
|
||||
### Node-Komponenten (`nodes/`)
|
||||
|
||||
| Datei | Zweck |
|
||||
|-------|-------|
|
||||
| `prompt-node.tsx` | KI-Bild-Steuer-Node mit Modell-Selector und Generate-Button |
|
||||
| `ai-image-node.tsx` | KI-Bild-Output-Node mit Bildvorschau, Metadaten, Retry |
|
||||
| `video-prompt-node.tsx` | KI-Video-Steuer-Node mit Modell-/Dauer-Selector, Credit-Anzeige, Generate-Button |
|
||||
| `ai-video-node.tsx` | KI-Video-Output-Node mit Video-Player, Metadaten, Retry-Button |
|
||||
|
||||
---
|
||||
|
||||
## Sidebar Resizing & Rail-Mode
|
||||
@@ -185,7 +219,7 @@ Im **Light Mode** wird der eigentliche Edge-`stroke` ebenfalls aus dieser Akzent
|
||||
- Resizing läuft über `react-resizable-panels` via `components/ui/resizable.tsx` in `canvas-shell.tsx`.
|
||||
- Wichtige Größen werden als **Strings mit Einheit** gesetzt (z. B. `"18%"`, `"40%"`, `"64px"`). In der verwendeten Library-Version werden numerische Werte als Pixel interpretiert.
|
||||
- Sidebar ist `collapsible`; bei Unterschreiten von `minSize` wird auf `collapsedSize` reduziert.
|
||||
- Eingeklappt bedeutet nicht „unsichtbar“: `collapsedSize` ist absichtlich > 0 (`64px`), damit ein sichtbarer Rail bleibt.
|
||||
- Eingeklappt bedeutet nicht „unsichtbar": `collapsedSize` ist absichtlich > 0 (`64px`), damit ein sichtbarer Rail bleibt.
|
||||
- `canvas-shell.tsx` schaltet per `onResize` abhängig von der tatsächlichen Pixelbreite zwischen Full-Mode und Rail-Mode um (`railMode` Prop an `CanvasSidebar`).
|
||||
- Im Full-Mode zeigt die Sidebar **nicht** mehr den Canvas-Namen, sondern das LemonSpace-Wordmark aus `public/logos/`:
|
||||
- Light Mode → `lemonspace-logo-v2-black-rgb.svg`
|
||||
@@ -200,6 +234,7 @@ Im **Light Mode** wird der eigentliche Edge-`stroke` ebenfalls aus dieser Akzent
|
||||
## 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.
|
||||
- **Video-Node `data.url`:** Gleiches Prinzip wie bei `ai-image` — Convex Storage URL wird über `batchGetUrlsForCanvas` aufgelöst. Video wird mit `<video src={data.url}>` abgespielt.
|
||||
- **Adjustment-Presets:** `curves`, `color-adjust`, `light-adjust` und `detail-adjust` dürfen keine eigene `presets.list`-Query feuern. Immer `CanvasPresetsProvider` + `useCanvasAdjustmentPresets(...)` verwenden.
|
||||
- **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.
|
||||
@@ -207,3 +242,5 @@ Im **Light Mode** wird der eigentliche Edge-`stroke` ebenfalls aus dieser Akzent
|
||||
- **`"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`.
|
||||
- **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.
|
||||
|
||||
@@ -10,7 +10,7 @@ Convex ist das vollständige Backend von LemonSpace: Datenbank, Realtime-Subscri
|
||||
|-------|-------|
|
||||
| `schema.ts` | Einzige Wahrheitsquelle für alle Tabellen und Typen |
|
||||
| `node_type_validator.ts` | Node-Typen Validator (Phase 1, Phase 2, Phase 3, Adjustment Presets) |
|
||||
| `ai.ts` | KI-Bildgenerierungs-Pipeline |
|
||||
| `ai.ts` | KI-Bild- und KI-Video-Generierungs-Pipeline |
|
||||
| `credits.ts` | Credit-System: Balance, Reservation+Commit, Tier-Config |
|
||||
| `nodes.ts` | CRUD für Canvas-Nodes |
|
||||
| `edges.ts` | CRUD für Canvas-Edges |
|
||||
@@ -22,7 +22,8 @@ Convex ist das vollständige Backend von LemonSpace: Datenbank, Realtime-Subscri
|
||||
| `canvas-connection-policy.ts` | Verbindungspolitiken zwischen Nodes (Validierung) |
|
||||
| `polar.ts` | Polar.sh Webhook-Handler (Subscriptions) |
|
||||
| `pexels.ts` | Pexels Stock-Bilder API |
|
||||
| `freepik.ts` | Freepik Asset-Browser API |
|
||||
| `freepik.ts` | Freepik Asset-Browser API + Video-Generierungs-Client |
|
||||
| `ai_utils.ts` | Gemeinsame Helpers für AI-Pipeline (z. B. `assertNodeBelongsToCanvasOrThrow`) |
|
||||
| `storage.ts` | Convex File Storage Helpers + gebündelte Canvas-URL-Auflösung |
|
||||
| `export.ts` | Canvas-Export-Logik |
|
||||
| `http.ts` | HTTP-Endpunkte (Webhooks) |
|
||||
@@ -48,6 +49,8 @@ Alle Node-Typen werden über Validators definiert: `phase1NodeTypeValidator`, `n
|
||||
| `prompt` | `content`, `model`, `modelTier` | KI-Generierungsanweisung |
|
||||
| `ai-image` | `storageId`, `prompt`, `model`, `modelTier`, `parameters`, `generationTimeMs`, `creditCost` | Generiertes KI-Bild |
|
||||
| `text-node` | `content` | Generierter KI-Text |
|
||||
| `video-prompt` | `content`, `modelId`, `durationSeconds` | KI-Video-Steuer-Node (Eingabe) |
|
||||
| `ai-video` | `storageId`, `prompt`, `model`, `modelLabel`, `durationSeconds`, `creditCost`, `generatedAt`, `taskId` (transient) | Generiertes KI-Video (System-Output) |
|
||||
| `compare` | `leftNodeId`, `rightNodeId`, `sliderPosition` | Vergleichs-Node |
|
||||
| `frame` | `label`, `exportWidth`, `exportHeight`, `backgroundColor` | Artboard |
|
||||
| `group` | `label`, `collapsed` | Container-Node |
|
||||
@@ -73,7 +76,7 @@ Alle Node-Typen werden über Validators definiert: `phase1NodeTypeValidator`, `n
|
||||
|
||||
**`creditBalances`** — Pro User: `balance`, `reserved`, `monthlyAllocation`. `available = balance - reserved` (computed, nicht gespeichert).
|
||||
|
||||
**`creditTransactions`** — Jede Credit-Bewegung. Types: `subscription | topup | usage | reservation | refund`. Status: `committed | reserved | released | failed`.
|
||||
**`creditTransactions`** — Jede Credit-Bewegung. Types: `subscription | topup | usage | reservation | refund`. Status: `committed | reserved | released | failed`. Optionale Felder: `provider` (`openrouter` | `freepik`), `videoMeta` (`model`, `durationSeconds`, `hasAudio`).
|
||||
|
||||
**`subscriptions`** — Aktive Subscription. Tier: `free | starter | pro | max | business`.
|
||||
|
||||
@@ -81,7 +84,7 @@ Alle Node-Typen werden über Validators definiert: `phase1NodeTypeValidator`, `n
|
||||
|
||||
---
|
||||
|
||||
## AI-Pipeline (`ai.ts`)
|
||||
## AI-Bild-Pipeline (`ai.ts`)
|
||||
|
||||
```
|
||||
generateImage (action, public)
|
||||
@@ -112,6 +115,67 @@ processImageGeneration (internalAction)
|
||||
|
||||
---
|
||||
|
||||
## KI-Video-Pipeline (`ai.ts`)
|
||||
|
||||
Analog zur Bild-Pipeline, aber mit Freepik als Provider und asynchronem Polling statt direktem API-Call.
|
||||
|
||||
```
|
||||
generateVideo (action, public)
|
||||
→ checkAbuseLimits (internalMutation)
|
||||
→ reserve (mutation, provider: "freepik", videoMeta: {...})
|
||||
→ markNodeExecuting (internalMutation)
|
||||
→ scheduler.runAfter(0, processVideoGeneration) ← Background-Job
|
||||
|
||||
processVideoGeneration (internalAction)
|
||||
→ createVideoTask (freepik.ts) ← POST an Freepik API
|
||||
→ setVideoTaskInfo (internalMutation) ← taskId am Node speichern
|
||||
→ scheduler.runAfter(5s, pollVideoTask) ← Polling starten
|
||||
|
||||
pollVideoTask (internalAction, max 30 Versuche / 10 Min)
|
||||
→ getVideoTaskStatus (freepik.ts) ← GET an modellspezifischen Endpunkt
|
||||
[COMPLETED] → downloadVideoAsBlob → ctx.storage.store(blob)
|
||||
→ finalizeVideoSuccess (internalMutation)
|
||||
→ commitInternal (credits)
|
||||
[FAILED] → releaseInternal (credits)
|
||||
→ finalizeVideoFailure (internalMutation)
|
||||
→ decrementConcurrency
|
||||
[CREATED|IN_PROGRESS] → scheduler.runAfter(delay, pollVideoTask, attempt+1)
|
||||
[retryable Fehler] → markVideoPollingRetry → scheduler.runAfter(delay, pollVideoTask, attempt+1)
|
||||
[nicht-retryable] → releaseInternal → finalizeVideoFailure → decrementConcurrency
|
||||
[Timeout/Max-Versuche] → releaseInternal → finalizeVideoFailure → decrementConcurrency
|
||||
```
|
||||
|
||||
**Wichtig:** `generateVideo` gibt `{ queued: true, outputNodeId }` zurück. Der Node wechselt sofort auf `status: "executing"`. Freepik erstellt einen Async-Task, der via Polling überwacht wird.
|
||||
|
||||
**Polling-Strategie:** Exponential Backoff in 3 Stufen:
|
||||
- Versuch 1–5: 5s Wartezeit
|
||||
- Versuch 6–15: 10s Wartezeit
|
||||
- Versuch 16–30: 20s Wartezeit
|
||||
- Max. 30 Versuche oder 10 Minuten Gesamt-Timeout → `FAILED`
|
||||
|
||||
**Modellspezifische Endpunkte:** Jedes Video-Modell hat einen eigenen Status-Endpunkt (`statusEndpointPath` in `lib/ai-video-models.ts`). Freepik hat **keinen** generischen `/v1/ai/tasks/{taskId}`-Endpunkt — der richtige Pfad ist z. B. `/v1/ai/text-to-video/wan-2-5-t2v-720p/{task-id}`.
|
||||
|
||||
**Freepik Response-Formate:** Endpunkte liefern unterschiedliche Formate:
|
||||
- `task_id` kann auf Root-Ebene oder unter `data.task_id` stehen
|
||||
- `generated`-Array kann Strings oder `{ url: string }`-Objekte enthalten
|
||||
- Beide Formate werden in `freepik.ts` defensiv geparsed
|
||||
|
||||
**Error-Klassen:**
|
||||
- `FreepikApiError` — Eigene Error-Klasse mit `source: "freepik"`, `status`, `code`, `retryable`
|
||||
- HTTP 404 während Polling → `transient` / `retryable: true` (Freepik eventual consistency)
|
||||
- HTTP 503 → `model_unavailable` / `retryable: true`
|
||||
- HTTP 401 → `model_unavailable` / `retryable: false`
|
||||
|
||||
**Log-Volumen-Steuerung:** Poll-Logs werden über `lib/video-poll-logging.ts` (`shouldLogVideoPollAttempt`, `shouldLogVideoPollResult`) auf Versuch 1, jeden 5. Versuch und Terminalzustände reduziert.
|
||||
|
||||
**env-Flags:**
|
||||
- `FREEPIK_API_KEY` — Freepik API-Key (Header: `x-freepik-api-key`)
|
||||
- `INTERNAL_CREDITS_ENABLED=true` — Credit-Reservation aktiviert
|
||||
|
||||
**Video-Modell-Registry:** Siehe `lib/ai-video-models.ts` — 5 MVP-Modelle, Tier-basierte Freigabe, Credit-Kosten pro Clip-Länge (5s/10s).
|
||||
|
||||
---
|
||||
|
||||
## Credit-System (`credits.ts`)
|
||||
|
||||
### Tier-Konfiguration (`TIER_CONFIG`)
|
||||
@@ -136,6 +200,22 @@ processImageGeneration (internalAction)
|
||||
|
||||
---
|
||||
|
||||
## Freepik Video-Client (`freepik.ts`)
|
||||
|
||||
`freepik.ts` enthält sowohl die bestehende Asset-Suche als auch den Video-Generierungs-Client.
|
||||
|
||||
### Video-Funktionen
|
||||
|
||||
- `createVideoTask({ endpoint, prompt, durationSeconds })` — POST an Freepik-Endpunkt, liefert `{ task_id }`
|
||||
- `getVideoTaskStatus({ taskId, statusEndpointPath, attempt })` — GET an modellspezifischen Status-Endpunkt, liefert `{ status, generated?, error? }`
|
||||
- `downloadVideoAsBlob(url)` — Lädt Video von Freepik CDN als Blob (zeitbegrenzte Signed URL!)
|
||||
- `mapFreepikError(status, body)` — Mappt HTTP-Status auf strukturierte `FreepikMappedError`-Objekte
|
||||
- `FreepikApiError` — Eigene Error-Klasse mit `source`, `status`, `code`, `retryable`, `body`
|
||||
|
||||
**Wichtig:** Freepik CDN-URLs sind zeitbegrenzt. `downloadVideoAsBlob` muss sofort nach `COMPLETED` aufgerufen werden. Das Video wird dauerhaft in Convex Storage gesichert.
|
||||
|
||||
---
|
||||
|
||||
## Auth (`helpers.ts`)
|
||||
|
||||
```typescript
|
||||
@@ -205,11 +285,12 @@ Wirft bei unauthentifiziertem Zugriff. Wird von allen Queries und Mutations genu
|
||||
- `getCanvasConnectionValidationMessage()` — Fehlermeldung bei ungültigen Verbindungen
|
||||
|
||||
**Validierungsregeln (aus `canvas-connection-policy.ts`):**
|
||||
- Source-Typ muss Output-Ports haben
|
||||
- Target-Typ muss Input-Ports haben
|
||||
- Source-Typ muss Output-Ports haben, Target-Typ muss Input-Ports haben
|
||||
- Keine self-loops (Edge von Node zu sich selbst)
|
||||
- Quelle: `image`, `text`, `note`, `group`, `compare`, `frame` → Source-Ports
|
||||
- Ziel: `ai-image`, `compare` → Target-Ports
|
||||
- Ziel: `ai-image`, `ai-video`, `compare` → Target-Ports
|
||||
- `video-prompt` → `ai-video` ✅ (einzige gültige Kombination für Video-Flow)
|
||||
- `ai-video` als Source für andere Nodes → ❌ (nur Compare)
|
||||
- Curves- und Adjustment-Node-Presets: Nur Presets nutzen, keine direkten Edges
|
||||
|
||||
---
|
||||
|
||||
@@ -13,7 +13,10 @@ Geteilte Hilfsfunktionen, Typ-Definitionen und Konfiguration. Keine React-Kompon
|
||||
| `canvas-node-types.ts` | TypeScript-Typen und Union-Typen für Canvas-Nodes |
|
||||
| `canvas-node-templates.ts` | Default-Daten für neue Nodes (beim Einfügen aus Palette) |
|
||||
| `canvas-connection-policy.ts` | Validierungsregeln für Edge-Verbindungen zwischen Nodes |
|
||||
| `ai-models.ts` | Client-seitige Modell-Definitionen (muss mit `convex/openrouter.ts` in sync bleiben) |
|
||||
| `ai-models.ts` | Client-seitige Bild-Modell-Definitionen (muss mit `convex/openrouter.ts` in sync bleiben) |
|
||||
| `ai-video-models.ts` | Video-Modell-Registry: 5 MVP-Modelle mit Endpunkten, Credit-Kosten, Tier-Zugang |
|
||||
| `video-poll-logging.ts` | Log-Volumen-Steuerung für Video-Polling (vermeidet excessive Konsolenausgabe) |
|
||||
| `tier-credits.ts` | Tier-Normalisierung (`normalizePublicTier`) für Video-Modell-Tier-Checks |
|
||||
| `image-formats.ts` | Aspect-Ratio-Strings, Node-Chrome-Höhen (`AI_IMAGE_NODE_HEADER_PX` etc.) |
|
||||
| `auth.ts` | Better Auth Server-Instanz |
|
||||
| `auth-server.ts` | Server-Helper: `getAuthUser()`, `getToken()` |
|
||||
@@ -40,8 +43,9 @@ Alle Adapter-Funktionen zwischen Convex-Datenmodell und React Flow. Details in `
|
||||
**Kritische Exports:**
|
||||
- `convexNodeToRF`, `convexEdgeToRF`, `convexEdgeToRFWithSourceGlow`
|
||||
- `convexNodeDocWithMergedStorageUrl` — URL-Injection für Storage-Bilder aus serverseitig aufgelöster URL-Map oder gecachtem Vorgängerzustand
|
||||
- `NODE_DEFAULTS` — Default-Größen und Daten per Node-Typ
|
||||
- `NODE_HANDLE_MAP` — Handle-IDs pro Node-Typ
|
||||
- `NODE_DEFAULTS` — Default-Größen und Daten per Node-Typ (inkl. `video-prompt` und `ai-video`)
|
||||
- `NODE_HANDLE_MAP` — Handle-IDs pro Node-Typ (inkl. `video-prompt-out/in` und `video-out/in`)
|
||||
- `SOURCE_NODE_GLOW_RGB` — Edge-Glow-Farben pro Source-Node-Typ (inkl. `video-prompt` und `ai-video`)
|
||||
- `computeBridgeCreatesForDeletedNodes` — Kanten-Reconnect nach Node-Löschung
|
||||
- `computeMediaNodeSize` — Dynamische Node-Größe basierend auf Bild-Dimensionen
|
||||
|
||||
@@ -62,6 +66,8 @@ ADJUSTMENT_PRESET_NODE_TYPES // Spezifische Adjustment-Presets
|
||||
|
||||
**Wichtig:** Dieser Datei und `convex/node_type_validator.ts` müssen immer synchron gehalten werden. Neue Nodes → Validator anpassen.
|
||||
|
||||
**Video-Typen:** `video-prompt` ist in `PHASE1_CANVAS_NODE_TYPES` und `CANVAS_NODE_TYPES` enthalten. `ai-video` ist in `CANVAS_NODE_TYPES` (Phase 2).
|
||||
|
||||
---
|
||||
|
||||
## `canvas-node-catalog.ts` — Node-Taxonomie
|
||||
@@ -78,7 +84,7 @@ isNodePaletteEnabled // true wenn: implementiert + kein systemOutput + Template
|
||||
|
||||
**Kategorien:**
|
||||
- `source` — Quelle (image, text, video, asset, color)
|
||||
- `ai-output` — KI-Ausgabe (prompt, ai-text, ai-video, agent-output)
|
||||
- `ai-output` — KI-Ausgabe (prompt, video-prompt, ai-text, ai-video, agent-output)
|
||||
- `transform` — Transformation (crop, bg-remove, upscale)
|
||||
- `image-edit` — Bildbearbeitung (adjustments)
|
||||
- `control` — Steuerung & Flow
|
||||
@@ -103,6 +109,7 @@ Default-Initial-Daten für neue Nodes beim Einfügen aus Palette.
|
||||
|
||||
- Erstellt durch die Node-Katalog-Einträge
|
||||
- Enthält default-Werte für `data`-Felder
|
||||
- `video-prompt` hat Default-Daten: `{ modelId: "wan-2-2-720p", durationSeconds: 5 }`
|
||||
- Wird von `canvas.tsx` verwendet beim Node-Create
|
||||
|
||||
---
|
||||
@@ -120,7 +127,11 @@ Regeln für erlaubte Verbindungen zwischen Node-Typen.
|
||||
- Source-Typ muss Output-Ports haben, Target-Typ muss Input-Ports haben
|
||||
- Keine self-loops (Edge von Node zu sich selbst)
|
||||
- Quelle: `image`, `text`, `note`, `group`, `compare`, `frame` → Source-Ports
|
||||
- Ziel: `ai-image`, `compare` → Target-Ports
|
||||
- Ziel: `ai-image`, `ai-video`, `compare` → Target-Ports
|
||||
- **Video-Flow:** `video-prompt → ai-video` ✅ (einzige gültige Kombination)
|
||||
- `ai-video` als Target akzeptiert nur `video-prompt` als Source (`ai-video-source-invalid`)
|
||||
- `video-prompt` als Source akzeptiert nur `ai-video` als Target (`video-prompt-target-invalid`)
|
||||
- `text → video-prompt` ✅ (Prompt-Quelle, über default-Handles)
|
||||
- Curves- und Adjustment-Node-Presets: Nur Presets nutzen, keine direkten Edges
|
||||
|
||||
---
|
||||
@@ -137,6 +148,56 @@ export const DEFAULT_MODEL_ID: string
|
||||
|
||||
---
|
||||
|
||||
## `ai-video-models.ts` — Video-Modell-Registry
|
||||
|
||||
Zentrale Definition aller KI-Video-Modelle mit Freepik-Endpunkten, Credit-Kosten und Tier-Zugang.
|
||||
|
||||
```typescript
|
||||
export type VideoModelId = "wan-2-2-480p" | "wan-2-2-720p" | "kling-std-2-1" | "seedance-pro-1080p" | "kling-pro-2-6"
|
||||
export type VideoModelTier = "free" | "starter" | "pro"
|
||||
export type VideoModelDurationSeconds = 5 | 10
|
||||
|
||||
export const VIDEO_MODELS: Record<VideoModelId, VideoModel>
|
||||
export const DEFAULT_VIDEO_MODEL_ID: VideoModelId // "wan-2-2-720p"
|
||||
```
|
||||
|
||||
**MVP-Modelle:**
|
||||
|
||||
| ID | Label | Tier | Endpunkt | 5s (Cr) | 10s (Cr) |
|
||||
|---|---|---|---|---|---|
|
||||
| `wan-2-2-480p` | WAN 2.2 480p | free | `/v1/ai/text-to-video/wan-2-5-t2v-720p` | 28 | 56 |
|
||||
| `wan-2-2-720p` | WAN 2.2 720p | free | `/v1/ai/text-to-video/wan-2-5-t2v-720p` | 52 | 104 |
|
||||
| `kling-std-2-1` | Kling Standard 2.1 | starter | `/v1/ai/image-to-video/kling-v2-1-std` | 50 | 100 |
|
||||
| `seedance-pro-1080p` | Seedance Pro 1080p | starter | `/v1/ai/video/seedance-1-5-pro-1080p` | 33 | 66 |
|
||||
| `kling-pro-2-6` | Kling Pro 2.6 | pro | `/v1/ai/image-to-video/kling-v2-6-pro` | 59 | 118 |
|
||||
|
||||
**Wichtig:** Jedes Modell hat einen `statusEndpointPath` (z. B. `/v1/ai/text-to-video/wan-2-5-t2v-720p/{task-id}`), der für das Polling des Task-Status verwendet wird. Freepik hat **keinen** generischen Task-Status-Endpunkt.
|
||||
|
||||
**Sync-Pflicht:** Diese Datei und `convex/freepik.ts` / `convex/ai.ts` müssen synchron gehalten werden. `creditCost` muss mit dem Credit-System übereinstimmen.
|
||||
|
||||
**Hilfsfunktionen:**
|
||||
- `isVideoModelId(value)` — Type-Guard für VideoModelId
|
||||
- `getVideoModel(id)` — Holt Modell-Definition, gibt `undefined` bei ungültiger ID
|
||||
- `getAvailableVideoModels(tier)` — Filtert Modelle nach User-Tier
|
||||
|
||||
---
|
||||
|
||||
## `video-poll-logging.ts` — Log-Volumen-Steuerung
|
||||
|
||||
Reduziert Konsolenausgabe beim Video-Polling (bis zu 30 Versuche pro Video).
|
||||
|
||||
```typescript
|
||||
export function shouldLogVideoPollAttempt(attempt: number): boolean
|
||||
// true bei Versuch 1 und jedem 5. Versuch (5, 10, 15, ...)
|
||||
|
||||
export function shouldLogVideoPollResult(attempt: number, status: VideoPollStatus): boolean
|
||||
// true bei jedem nicht-IN_PROGRESS Status oder wenn shouldLogVideoPollAttempt true ist
|
||||
```
|
||||
|
||||
Verwendet in `convex/ai.ts` (`pollVideoTask`) und `convex/freepik.ts` (`getVideoTaskStatus`).
|
||||
|
||||
---
|
||||
|
||||
## `canvas-local-persistence.ts` — localStorage-Cache
|
||||
|
||||
```typescript
|
||||
@@ -191,3 +252,4 @@ authClient // Better Auth Client-Instanz für signIn, signUp, signOut etc.
|
||||
- Keine React-Imports in `lib/` — reines TypeScript
|
||||
- `utils.ts` für generische Helpers (`cn`, `clamp`, etc.)
|
||||
- Typen, die sowohl Frontend als auch Convex betreffen, gehören in `lib/`, nicht in `convex/`
|
||||
- Modell-Registrys (`ai-models.ts`, `ai-video-models.ts`) müssen immer mit den Backend-Äquivalenten synchron gehalten werden
|
||||
|
||||
Reference in New Issue
Block a user