The board's symbol takes a tint — a 4×2 colour row under the picker's glyphs, and the glyph itself moves into the titlebar

iconColor stops being hand-written-only (user-ruled, superseding 03's
"schema yes, control no"): it rides StyleCommand.apply → applyStyle as
the third styled dimension — per-dimension no-op skip, one bracket, one
history step, ExpectedField.iconColor for staleness. The SymbolPicker
grows an opt-in colour row (leading None plus seven Palette.foregrounds
hues, None removes the key); the board popover is its one caller. The
window-title widget now draws the board's resolved glyph in that tint
beside the name. Doc realignment filed on the Redesign board (Minor).

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-08-07 18:36:49 -04:00
parent 3d231d6454
commit 7ac34651a2
12 changed files with 342 additions and 23 deletions
+30 -4
View File
@@ -33,7 +33,8 @@ private func styled(order: String, title: String, keys: [String] = []) -> String
}
/// The board root's own `index.md` styled like any other item (`StyleTarget.board`), and carrying
/// an `iconColor` the app must never touch (schema yes, control no).
/// an `iconColor` a gesture that never names that dimension must leave alone (it stopped being
/// hand-written-only when the board popover's symbol picker grew its colour row, 2026-08-07).
private let boardIndex = """
---
schema: 1
@@ -143,13 +144,38 @@ struct StyleWriteTests {
let after = try fixture.indexText("\(Ident.lane1)/\(Ident.card1)")
#expect(after.contains("background: {color: \"dark-teal\"}"))
#expect(after.contains("icon: flag"))
// "iconColor: resolved schema yes, control no" (03 § Styling Capabilities): the field
// renders when hand-written and the app offers no control for it, so a style write must
// carry it through untouched like any unknown key.
// The third dimension is a control now (the symbol picker's colour row), but a gesture
// that never names it must still carry it through untouched per-dimension `.keep`.
#expect(after.contains("iconColor: chalk"))
#expect(!after.contains("fern"), "the old value is replaced, not duplicated")
}
@Test("The colour row's dimension: iconColor sets as a plain scalar, removes as an absent key, and no-ops in place")
func iconColorIsTheThirdDimension() throws {
let fixture = try makeBoard()
defer { fixture.tearDown() }
let store = try BoardStore(rootURL: fixture.root)
let log = BracketLog()
log.attach(to: store)
// The fixture's board already carries `iconColor: carnation` re-choosing it is a no-op
// against the loaded snapshot, `skipsNoOps`'s rule at the third dimension.
store.applyStyle(to: .board, iconColor: .set("carnation"))
#expect(log.begins == 0, "re-choosing the tint an item already carries opens no bracket")
store.applyStyle(to: .board, iconColor: .set("fern"))
let set = try fixture.indexText("")
#expect(set.contains("iconColor: fern"))
#expect(!set.contains("carnation"), "the old tint is replaced, not duplicated")
#expect(!set.contains("icon: "), "the untouched icon dimension writes no key of its own")
store.applyStyle(to: .board, iconColor: .remove)
let removed = try fixture.indexText("")
#expect(!removed.contains("iconColor"))
#expect(!removed.contains("\"\""), "a removal is a missing key, never an empty string")
#expect(store.banners.oneShots.isEmpty)
}
@Test("The None and default wells remove their key rather than writing a blank value")
func removesTheKey() throws {
let fixture = try makeBoard()