Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 460b05f187 | |||
| 022086beb8 |
@@ -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
|
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.
|
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"`.
|
Canvas schema 1 also supports frames: `type: "frame"` with `data.kind: "frame"`.
|
||||||
Frame names use `data.title`; optional `data.labelMode` is `hidden`, `fixed`, or
|
Frame names use `data.title`; optional `data.labelMode` is `hidden`, `fixed`, or
|
||||||
`scaled` (frontend default: `fixed`). Frames cannot rotate, connect to edges,
|
`scaled` (frontend default: `fixed`). Frames cannot rotate, connect to edges,
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ func normalizeCanvas(raw json.RawMessage) (json.RawMessage, bool) {
|
|||||||
typ := "material"
|
typ := "material"
|
||||||
if kind == "plain-text" {
|
if kind == "plain-text" {
|
||||||
typ = "plainText"
|
typ = "plainText"
|
||||||
|
} else if kind == "plain-image" {
|
||||||
|
typ = "plainImage"
|
||||||
} else if kind == "icon" {
|
} else if kind == "icon" {
|
||||||
typ = "icon"
|
typ = "icon"
|
||||||
} else if kind == "frame" {
|
} else if kind == "frame" {
|
||||||
@@ -172,7 +174,7 @@ func normalizeCanvas(raw json.RawMessage) (json.RawMessage, bool) {
|
|||||||
return nil, false
|
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
|
return nil, false
|
||||||
}
|
}
|
||||||
if value, exists := d["textStyle"]; exists {
|
if value, exists := d["textStyle"]; exists {
|
||||||
|
|||||||
@@ -88,3 +88,20 @@ func TestCanvasFrames(t *testing.T) {
|
|||||||
t.Fatal("optional labelMode rejected")
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user