- Added remote image patterns to the Next.js configuration for enhanced image handling. - Updated TypeScript configuration to exclude the 'implement' directory. - Refactored layout component to fetch initial authentication token and pass it to Providers. - Replaced CanvasToolbar with CanvasSidebar for improved UI layout and functionality. - Enhanced Canvas component with new drag-and-drop file upload capabilities and batch node movement. - Updated various node components to support new status handling and improved user interactions. - Added debounced saving for note and prompt nodes to optimize performance.
104 lines
3.5 KiB
Markdown
104 lines
3.5 KiB
Markdown
# Bild-Upload via Convex Storage — Einbau-Anleitung
|
|
|
|
## Konzept
|
|
|
|
Der Upload-Flow nutzt Convex File Storage in 3 Schritten:
|
|
1. **generateUploadUrl** → kurzlebige Upload-URL vom Backend
|
|
2. **fetch(POST)** → Datei direkt an Convex Storage senden
|
|
3. **updateData** → `storageId` im Node speichern
|
|
|
|
Die **URL wird serverseitig** in der `nodes.list` Query aufgelöst — nicht
|
|
am Client. Das heißt: der Node speichert nur die `storageId`, und bei
|
|
jedem Query-Aufruf wird `ctx.storage.getUrl(storageId)` aufgerufen und
|
|
als `data.url` zurückgegeben.
|
|
|
|
## Dateien
|
|
|
|
```
|
|
upload-files/
|
|
convex/
|
|
storage.ts → convex/storage.ts (NEU)
|
|
nodes-list-patch.ts → PATCH für convex/nodes.ts (NUR die list Query ersetzen)
|
|
components/canvas/nodes/
|
|
image-node.tsx → ERSETZT alte Version
|
|
|
|
Gesamt: 3 Dateien (1 neu, 1 Patch, 1 Ersatz)
|
|
```
|
|
|
|
## Einbau-Schritte
|
|
|
|
### 1. `convex/storage.ts` anlegen
|
|
Kopiere die Datei direkt. Sie enthält eine einzige Mutation: `generateUploadUrl`.
|
|
|
|
### 2. `convex/nodes.ts` — `list` Query patchen
|
|
Ersetze **nur die `list` Query** in deiner bestehenden `convex/nodes.ts`
|
|
mit der Version aus `nodes-list-patch.ts`. Der Rest der Datei
|
|
(create, move, resize, etc.) bleibt unverändert.
|
|
|
|
Die Änderung: Nach dem `collect()` wird über alle Nodes iteriert.
|
|
Wenn ein Node `data.storageId` hat, wird `ctx.storage.getUrl()` aufgerufen
|
|
und das Ergebnis als `data.url` eingefügt.
|
|
|
|
**Wichtig:** Du brauchst den `Id` Import oben in der Datei:
|
|
```ts
|
|
import type { Doc, Id } from "./_generated/dataModel";
|
|
```
|
|
(Du hast `Doc` wahrscheinlich schon importiert — füge `Id` hinzu falls nötig.)
|
|
|
|
### 3. `image-node.tsx` ersetzen
|
|
Die neue Version hat:
|
|
- **Click-to-Upload**: Klick auf den leeren Node öffnet File-Picker
|
|
- **Drag & Drop**: Bilder direkt auf den Node ziehen (Files vom OS)
|
|
- **Ersetzen-Button**: Wenn bereits ein Bild vorhanden, oben rechts "Ersetzen"
|
|
- **Upload-Spinner**: Während des Uploads dreht sich ein Spinner
|
|
- **Dateiname**: Wird unter dem Bild angezeigt
|
|
|
|
## Upload-Flow im Detail
|
|
|
|
```
|
|
User zieht Bild auf Image-Node
|
|
│
|
|
├─ handleDrop() → uploadFile(file)
|
|
│
|
|
├─ 1. generateUploadUrl() → Convex Mutation
|
|
│ ← postUrl (kurzlebig)
|
|
│
|
|
├─ 2. fetch(postUrl, { body: file })
|
|
│ ← { storageId: "kg..." }
|
|
│
|
|
├─ 3. updateData({ nodeId, data: { storageId, filename, mimeType } })
|
|
│ → Convex speichert storageId im Node
|
|
│
|
|
└─ 4. nodes.list Query feuert automatisch neu (Realtime)
|
|
→ ctx.storage.getUrl(storageId) → data.url
|
|
→ Image-Node rendert das Bild
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Test 1: Click-to-Upload
|
|
- Erstelle einen Image-Node (Sidebar oder Toolbar)
|
|
- Klicke auf "Klicken oder hierhin ziehen"
|
|
- ✅ File-Picker öffnet sich
|
|
- Wähle ein Bild (PNG/JPG/WebP)
|
|
- ✅ Spinner erscheint kurz, dann wird das Bild angezeigt
|
|
- ✅ Convex Dashboard: `data.storageId` ist gesetzt
|
|
|
|
### Test 2: Drag & Drop (File vom OS)
|
|
- Ziehe ein Bild aus dem Finder/Explorer direkt auf den Image-Node
|
|
- ✅ Drop-Zone wird blau hervorgehoben
|
|
- ✅ Bild wird hochgeladen und angezeigt
|
|
|
|
### Test 3: Bild ersetzen
|
|
- Klicke "Ersetzen" oben rechts am Image-Node
|
|
- Wähle ein neues Bild
|
|
- ✅ Altes Bild wird ersetzt, neue storageId in Convex
|
|
|
|
### Test 4: URL wird serverseitig aufgelöst
|
|
- Lade die Seite neu
|
|
- ✅ Bild wird weiterhin angezeigt (URL wird bei jedem Query neu aufgelöst)
|
|
|
|
### Test 5: Nicht-Bild-Dateien werden ignoriert
|
|
- Versuche eine .txt oder .pdf auf den Node zu ziehen
|
|
- ✅ Nichts passiert (nur image/* wird akzeptiert)
|