feat(canvas): build PSD export in the browser from canvas layers

Move PSD generation off the server. The image-generation API only
produces raster formats, so drop the PSD request path, the oov/psd
decoder, and the LayeredDocumentGenerator wiring. Layer separation now
returns clean background, foreground, and text_render_data artifacts
into a transparent frame.

Export that frame (or any image node) as PSD from the canvas context
menu: ag-psd assembles layers, rotation/flip, opacity, editable text,
and stroke effects from the current canvas state at download time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 11:01:46 +08:00
parent 4ff246a294
commit 60130b3d9b
17 changed files with 424 additions and 614 deletions
+19 -24
View File
@@ -51,26 +51,25 @@ const nodeActionResultNodeIDOption = "_resultNodeId"
const generationRecoveryToolHint = "generation_recovery"
type DesignService struct {
repo design.Repository
agent design.AgentRunner
assets design.AssetStorage
assetCleanup *assetCleanupQueue
jobs design.JobQueue
search design.Researcher
textExtractor design.TextExtractor
objectRecognizer design.ObjectRecognizer
imageVectorizer design.ImageVectorizer
backgroundRemover design.BackgroundRemover
layeredDocumentGenerator design.LayeredDocumentGenerator
mockupAnalyzer design.MockupModelAnalyzer
textExtractionCache textExtractionCacheStore
textExtractionCacheTTL time.Duration
creativeAgent design.CreativeAgent
cancelledAgentTasks sync.Map
agentTaskCancels sync.Map
generatorTaskMu sync.RWMutex
generatorTasks map[string]design.GeneratorTask
now func() time.Time
repo design.Repository
agent design.AgentRunner
assets design.AssetStorage
assetCleanup *assetCleanupQueue
jobs design.JobQueue
search design.Researcher
textExtractor design.TextExtractor
objectRecognizer design.ObjectRecognizer
imageVectorizer design.ImageVectorizer
backgroundRemover design.BackgroundRemover
mockupAnalyzer design.MockupModelAnalyzer
textExtractionCache textExtractionCacheStore
textExtractionCacheTTL time.Duration
creativeAgent design.CreativeAgent
cancelledAgentTasks sync.Map
agentTaskCancels sync.Map
generatorTaskMu sync.RWMutex
generatorTasks map[string]design.GeneratorTask
now func() time.Time
}
type agentTaskCancelHandle struct {
@@ -137,10 +136,6 @@ func (s *DesignService) SetBackgroundRemover(remover design.BackgroundRemover) {
s.backgroundRemover = remover
}
func (s *DesignService) SetLayeredDocumentGenerator(generator design.LayeredDocumentGenerator) {
s.layeredDocumentGenerator = generator
}
func (s *DesignService) SetObjectRecognizer(recognizer design.ObjectRecognizer) {
s.objectRecognizer = recognizer
}