The board wears a picture — background becomes a mapping, and the window chrome follows it under a thin frost

background is {color:, image:} and only a mapping at every level; the board's image paints the full window under a transparent title bar, with a thin-material frost strip keeping the chrome legible and the standard accommodations intact.

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-07 10:15:03 -04:00
parent 190a8e36f1
commit d5ad21c3da
37 changed files with 1182 additions and 76 deletions
+115 -6
View File
@@ -551,8 +551,8 @@ struct FrontmatterLenientFieldTests {
@Test func wellFormedLenientValues() throws {
#expect(try document("title: My Board").title == .valid("My Board"))
#expect(try document("background: \"#ff8800\"").background == .valid("#ff8800"))
#expect(try document("background: slate").background == .valid("slate"))
#expect(try document("background: {color: \"#ff8800\"}").background == .valid("#ff8800"))
#expect(try document("background: {color: slate}").background == .valid("slate"))
#expect(try document("icon: tray.full").icon == .valid("tray.full"))
#expect(try document("iconColor: teal").iconColor == .valid("teal"))
#expect(try document("width: 3").width == .valid(3))
@@ -565,7 +565,7 @@ struct FrontmatterLenientFieldTests {
@Test func scalarsOfTheWrongTypeCoerceToTheirSourceText() throws {
#expect(try document("title: 2048").title == .valid("2048"))
#expect(try document("title: true").title == .valid("true"))
#expect(try document("background: 42").background == .valid("42"))
#expect(try document("background: {color: 42}").background == .valid("42"))
#expect(try document("iconColor: true").iconColor == .valid("true"))
#expect(try document("icon: 2026-07-26T16:41:38Z").icon == .valid("2026-07-26T16:41:38Z"))
}
@@ -575,7 +575,7 @@ struct FrontmatterLenientFieldTests {
@Test func aTrailingCommentIsNotPartOfACoercedValue() throws {
#expect(try document("title: 2048 # note").title == .valid("2048"))
#expect(try document("title: true # note").title == .valid("true"))
#expect(try document("background: 42\t# tabbed").background == .valid("42"))
#expect(try document("background: {color: 42}\t# tabbed").background == .valid("42"))
#expect(try document("icon: 2026-07-26T16:41:38Z # when").icon == .valid("2026-07-26T16:41:38Z"))
}
@@ -583,8 +583,8 @@ struct FrontmatterLenientFieldTests {
/// at all, so a read must not treat it as a comment.
@Test func aHashInsideAQuotedValueIsNotTrimmed() throws {
#expect(try document("title: \"2048 # note\"").title == .valid("2048 # note"))
#expect(try document("background: \"#ff8800\"").background == .valid("#ff8800"))
#expect(try document("background: \"#ff8800\" # brand orange").background == .valid("#ff8800"))
#expect(try document("background: {color: \"#ff8800\"}").background == .valid("#ff8800"))
#expect(try document("background: {color: \"#ff8800\"} # brand orange").background == .valid("#ff8800"))
}
/// A sequence or mapping has no scalar reading at all; the raw text it falls back to stops at
@@ -608,6 +608,115 @@ struct FrontmatterLenientFieldTests {
#expect(try document("title: [a, b]").title == .malformed(raw: "[a, b]"))
}
// MARK: `background` is a mapping and only a mapping
/// **The retired scalar** (01-storage-format.md § Frontmatter, ruled 2026-08-06 before anything
/// shipped one shape, no legacy spelling, no migration): `background: green` has no reading at
/// all. It is `.malformed` like any other unreadable value, which renders as no colour and
/// leaves the bytes exactly as written.
@Test func aScalarBackgroundHasNoReading() throws {
#expect(try document("background: green").background == .malformed(raw: "green"))
#expect(try document("background: \"#ff8800\"").background == .malformed(raw: "\"#ff8800\""))
#expect(try document("background: 42").background == .malformed(raw: "42"))
// The raw stops at the comment like every other read, since a comment is the line's.
#expect(try document("background: green # my colour").background == .malformed(raw: "green"))
}
/// One unreadable value is reported **once**: the colour reading owns the key's shape, and the
/// image stays silent about a file that never wrote a mapping to name a picture in.
@Test func onlyTheColourReportsANonMappingShape() throws {
#expect(try document("background: green").backgroundImage == .missing)
#expect(try document("background: [red, blue]").backgroundImage == .missing)
#expect(try document("background: 42").backgroundImage == .missing)
#expect(try document("schema: 1").backgroundImage == .missing)
#expect(try document("background: green").coercedFields
== [CoercedField(key: "background", raw: "green")])
}
/// The mapping form, both halves present flow and block spellings are one YAML value and
/// therefore one reading.
@Test func aMappingBackgroundReadsBothSubkeys() throws {
let flow = try document("background: {color: \"#112233\", image: sunset.jpg}")
#expect(flow.background == .valid("#112233"))
#expect(flow.backgroundImage == .valid("sunset.jpg"))
let block = try FrontmatterDocument.parse(
"---\nbackground:\n color: fern\n image: art/sunset.jpg\n---\nbody\n"
)
#expect(block.background == .valid("fern"))
#expect(block.backgroundImage == .valid("art/sunset.jpg"))
}
/// Either half may be absent, and an absent half is `.missing` not malformed. An image-only
/// background is a board with no colour, which is the level's default and not a fallback.
@Test func eitherSubkeyMayBeAbsent() throws {
let colorOnly = try document("background: {color: chalk}")
#expect(colorOnly.background == .valid("chalk"))
#expect(colorOnly.backgroundImage == .missing)
let imageOnly = try document("background: {image: sunset.jpg}")
#expect(imageOnly.background == .missing)
#expect(imageOnly.backgroundImage == .valid("sunset.jpg"))
let empty = try document("background: {}")
#expect(empty.background == .missing)
#expect(empty.backgroundImage == .missing)
}
/// An explicit null subkey reads exactly like an absent one `FieldValue.missing` already
/// treats `background: null` that way, and a subkey is no different.
@Test func nullSubkeysReadAsMissing() throws {
let nulls = try document("background: {color: null, image: ~}")
#expect(nulls.background == .missing)
#expect(nulls.backgroundImage == .missing)
}
/// Unknown subkeys are tolerated on the way in exactly as unknown *keys* are they mean
/// nothing to either reading and cost it nothing.
@Test func unknownSubkeysAreTolerated() throws {
let extra = try document("background: {opacity: 0.5, color: fern, blend: multiply}")
#expect(extra.background == .valid("fern"))
#expect(extra.backgroundImage == .missing)
}
/// Inside the mapping the subvalues coerce like any other scalar, quoted or not; a subvalue with
/// no scalar reading at all is malformed, quoting the subvalue rather than the whole span
/// a subkey has no source span of its own to quote.
@Test func subkeyScalarsCoerceAndCollectionsAreMalformed() throws {
#expect(try document("background: {color: 42}").background == .valid("42"))
#expect(try document("background: {image: \"sun set.jpg\"}").backgroundImage == .valid("sun set.jpg"))
#expect(try document("background: {color: [a, b]}").background == .malformed(raw: "[a, b]"))
#expect(try document("background: {image: {a: 1}}").backgroundImage == .malformed(raw: "{a: 1}"))
// The other half of a mapping with one bad subkey still reads perfectly well.
#expect(try document("background: {color: [a, b], image: sunset.jpg}").backgroundImage == .valid("sunset.jpg"))
}
/// **The coerce tier's trace covers both readings** (01-storage-format.md § Frontmatter: "every
/// silent recovery leaves a trace"). Both are filed under the key the schema spells, and are told
/// apart by the subvalue each quotes so a mapping whose colour reads fine and whose image does
/// not is still visible.
@Test func bothBackgroundReadingsReachTheCoerceRecord() throws {
#expect(try document("background: {color: fern, image: [a, b]}").coercedFields
== [CoercedField(key: "background", raw: "[a, b]")])
#expect(try document("background: {color: [a], image: [b]}").coercedFields
== [CoercedField(key: "background", raw: "[a]"), CoercedField(key: "background", raw: "[b]")])
// A sequence is one unreadable value and is reported once the image reading stays silent
// about a shape that never claimed to name one.
#expect(try document("background: [red, blue]").coercedFields
== [CoercedField(key: "background", raw: "[red, blue]")])
#expect(try document("background: {image: sunset.jpg}").coercedFields.isEmpty)
}
/// The engine reads the shape; it never rewrites it. A mapping background round-trips
/// byte-identically like every other value the app did not touch.
@Test func aMappingBackgroundRoundTripsVerbatim() throws {
let text = "---\nschema: 1\nbackground:\n color: fern\n image: art/sunset.jpg\n blend: multiply\n---\nbody\n"
let document = try FrontmatterDocument.parse(text)
#expect(document.serialized() == text)
#expect(document.background == .valid("fern"))
#expect(document.backgroundImage == .valid("art/sunset.jpg"))
}
/// Only a fractional or non-numeric reading has no sensible width at all a sequence,
/// mapping, or scalar with no integer reading whatsoever stays malformed and renders as the
/// default 1.