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:
@@ -11,8 +11,6 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"img_infinite_canvas/internal/domain/design"
|
||||
)
|
||||
|
||||
func TestNewImageClientDefaultsToTenMinuteTimeout(t *testing.T) {
|
||||
@@ -167,62 +165,36 @@ func TestImageClientSendsQuality(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageClientSendsOutputFormatAndParsesPSDBase64(t *testing.T) {
|
||||
func TestNormalizeImageOutputFormatRejectsPSD(t *testing.T) {
|
||||
if got := normalizeImageOutputFormat("psd"); got != "" {
|
||||
t.Fatalf("expected unsupported PSD output format to be omitted, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageClientSendsOutputFormatAndParsesWebPBase64(t *testing.T) {
|
||||
var requestBody map[string]any
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"data":[{"b64_json":"cHNkLWJ5dGVz"}]}`))
|
||||
_, _ = w.Write([]byte(`{"data":[{"b64_json":"d2VicC1ieXRlcw=="}]}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewImageClient(ImageClientOptions{BaseURL: server.URL, APIKey: "test-key"})
|
||||
image, err := client.Generate(context.Background(), "create layered file", ImageRequestOptions{Model: "gpt-image-2", OutputFormat: "psd"})
|
||||
image, err := client.Generate(context.Background(), "create an image", ImageRequestOptions{Model: "gpt-image-2", OutputFormat: "webp"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if requestBody["output_format"] != "psd" {
|
||||
t.Fatalf("expected output_format=psd, got %#v", requestBody["output_format"])
|
||||
if requestBody["output_format"] != "webp" {
|
||||
t.Fatalf("expected output_format=webp, got %#v", requestBody["output_format"])
|
||||
}
|
||||
if requestBody["stream"] != nil {
|
||||
t.Fatalf("expected psd output to avoid streaming, got %#v", requestBody["stream"])
|
||||
t.Fatalf("expected explicit output format to avoid streaming, got %#v", requestBody["stream"])
|
||||
}
|
||||
if image.URL != "data:image/vnd.adobe.photoshop;base64,cHNkLWJ5dGVz" {
|
||||
t.Fatalf("expected psd data url, got %#v", image)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageClientGenerateLayeredDocumentUsesPSDOutput(t *testing.T) {
|
||||
var requestBody map[string]any
|
||||
var requestPath string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
requestPath = r.URL.Path
|
||||
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"data":[{"url":"https://example.com/layers.psd"}]}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewImageClient(ImageClientOptions{BaseURL: server.URL, APIKey: "test-key", InputImageTransport: "url"})
|
||||
document, err := client.GenerateLayeredDocument(context.Background(), design.LayeredDocumentRequest{
|
||||
ImageURL: "https://example.com/source.png",
|
||||
Prompt: "separate layers",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if requestPath != "/v1/images/edits" {
|
||||
t.Fatalf("expected image edit endpoint, got %s", requestPath)
|
||||
}
|
||||
if requestBody["output_format"] != "psd" || requestBody["size"] != "auto" {
|
||||
t.Fatalf("expected psd auto request, got %#v", requestBody)
|
||||
}
|
||||
if document.Content != "https://example.com/layers.psd" || document.ContentType != "image/vnd.adobe.photoshop" {
|
||||
t.Fatalf("unexpected layered document: %#v", document)
|
||||
if image.URL != "data:image/webp;base64,d2VicC1ieXRlcw==" {
|
||||
t.Fatalf("expected WebP data URL, got %#v", image)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user