feat: 无限画布 — infinite canvas with generation integration

Backend:
- canvases table + owner-scoped CRUD under /admin/api/canvas
  (list/create/get/save/delete, 50 docs/user, 4MiB cap, JSON validation)

Frontend:
- /canvas full-screen workspace: DOM+CSS-transform infinite pan/zoom
  (GPU-composited world layer, viewport culling, rAF-batched pointer moves)
- nodes: image (thumb display, natural-size correction), note
  (uncontrolled contenteditable), generation placeholder with edges
- generate on canvas: prompt from panel or selected notes, selected image
  nodes as references, results auto-placed + bezier edges to sources
- multi-canvas management, debounced autosave + keepalive flush,
  snapshot undo/redo (60 steps), marquee/keyboard interactions
This commit is contained in:
dokploy-bot
2026-09-19 22:38:41 +08:00
parent 42208c0753
commit 571ee2c801
12 changed files with 2078 additions and 1 deletions
+13
View File
@@ -250,6 +250,7 @@ func AutoMigrateModels() []any {
&StatCounter{},
&ConcurrencyGroup{},
&Order{},
&Canvas{},
}
}
@@ -289,6 +290,18 @@ type ConcurrencyGroup struct {
UpdatedAt time.Time
}
// Canvas is one user's infinite-canvas document. Data stores the whole
// node/edge graph as a versioned JSON blob; saves are whole-document
// replacements (last write wins), which matches the single-editor frontend.
type Canvas struct {
ID string `gorm:"primaryKey;size:40"`
UserID string `gorm:"size:32;index;not null"`
Title string `gorm:"size:120;not null;default:'未命名画布'"`
Data string `gorm:"type:text"`
CreatedAt time.Time
UpdatedAt time.Time `gorm:"index"`
}
// StatCounter is a persistent monotonic counter (key → value), independent of the
// event_log (which is retention-pruned / clearable). Used for the dashboard
// cumulative cards (total/success/failed/image/video/api) so they never reset.