Compare commits

..

2 Commits

Author SHA1 Message Date
codex-bot 460b05f187 feat(canvas): support unframed image snapshot nodes
Foundry Go checks / check (push) Successful in 3m24s
Foundry Go checks / check (pull_request) Successful in 3m27s
2026-11-06 12:38:45 +08:00
Zayden-Jung 022086beb8 Merge pull request '支持 Canvas 框选区、成员归属与名称显示模式' (#9) from codex/canvas-frames into main
Foundry Go checks / check (push) Successful in 3m28s
Deploy Foundry Core / deploy (push) Successful in 3m4s
Reviewed-on: #9
2026-11-06 03:21:48 +00:00
3 changed files with 25 additions and 1 deletions
+5
View File
@@ -105,6 +105,11 @@ serialize autosaves and handle 412 explicitly. Restore node style dimensions
from width/height and render edges as Bézier curves. These frontend changes are
outside this Core delivery; local drafts alone are not database persistence.
Canvas schema 1 supports unframed images: `type: "plainImage"` with
`data.kind: "plain-image"`. They require the same validated `data.src` as image
cards, support geometry/rotation/frame membership, and cannot connect to edges.
This is a snapshot-format extension with no additional migration.
Canvas schema 1 also supports frames: `type: "frame"` with `data.kind: "frame"`.
Frame names use `data.title`; optional `data.labelMode` is `hidden`, `fixed`, or
`scaled` (frontend default: `fixed`). Frames cannot rotate, connect to edges,
+3 -1
View File
@@ -135,6 +135,8 @@ func normalizeCanvas(raw json.RawMessage) (json.RawMessage, bool) {
typ := "material"
if kind == "plain-text" {
typ = "plainText"
} else if kind == "plain-image" {
typ = "plainImage"
} else if kind == "icon" {
typ = "icon"
} else if kind == "frame" {
@@ -172,7 +174,7 @@ func normalizeCanvas(raw json.RawMessage) (json.RawMessage, bool) {
return nil, false
}
}
if src, exists := d["src"]; (exists || kind == "image") && !canvasImage(src) {
if src, exists := d["src"]; (exists || kind == "image" || kind == "plain-image") && !canvasImage(src) {
return nil, false
}
if value, exists := d["textStyle"]; exists {
+17
View File
@@ -88,3 +88,20 @@ func TestCanvasFrames(t *testing.T) {
t.Fatal("optional labelMode rejected")
}
}
func TestCanvasPlainImages(t *testing.T) {
fixture := `{"schema_version":1,"nodes":[{"id":"photo","type":"plainImage","position":{"x":20,"y":30},"width":320,"height":240,"data":{"kind":"plain-image","title":"Photo","text":"","src":"https://example.com/photo.png","rotation":30,"frameId":"frame"}},{"id":"frame","type":"frame","position":{"x":0,"y":0},"width":640,"height":400,"data":{"kind":"frame","title":"Frame","text":"","labelMode":"fixed"}}],"edges":[],"viewport":{"x":0,"y":0,"zoom":1}}`
if _, ok := normalizeCanvas(json.RawMessage(fixture)); !ok {
t.Fatal("plain image rejected")
}
for _, change := range [][2]string{
{`"type":"plainImage"`, `"type":"material"`},
{`,"src":"https://example.com/photo.png"`, ``},
{`"src":"https://example.com/photo.png"`, `"src":"javascript:bad"`},
{`"edges":[]`, `"edges":[{"id":"e","source":"photo","target":"photo"}]`},
} {
if _, ok := normalizeCanvas(json.RawMessage(strings.Replace(fixture, change[0], change[1], 1))); ok {
t.Fatal("invalid pure image accepted")
}
}
}