replace symbol grid in card window sidebar with a compact symbol picker
The card window sidebar's Style section embedded StyleEditorView symbols-only (showsBackground: false, showsSymbols: true), drawing the curated grid whole inside the sidebar's own scroll view — a permanently open 5-7 row grid ahead of every other section. Replace it with the reusable compact SymbolPicker (already used by BoardInfoPopover for the board's glyph): a single well at rest, the grid only inside its own popover. - CardStyleSection no longer instantiates StyleEditorView at all. A new "Symbol" row mirrors the existing "Background" row's inspector shape (caption leading, control trailing), wiring SymbolPicker's onSelect/ onSelectColor to icon/iconColor through StyleCommand.apply(...on: undo), the exact funnel the background combo already rides — so the card window's undo session semantics (13-native-undo.md) are unchanged. - The picker is fed CuratedSymbols.available rather than its own smaller general-purpose default, so a card's curated vocabulary doesn't shrink. - StyleEditorLayout.sidebar and showsBackground stay in StyleEditor.swift (still correct, still tested) rather than being cut as dead code — a separate, larger cleanup this card doesn't make (recorded on the card). - DESIGN/03-board-ui.md and DESIGN/05-card-window.md: updated the sentences describing the sidebar hosting the style editor's symbol grid to describe the compact picker instead. - Tests: CardSessionUndoTests gains a symbol/tint analogue of the existing background-combo session-routing test; CardSidebarTests gains a curated-set tripwire and an updated file-header note. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -109,6 +109,19 @@ private func body(_ fixture: WriterFixture, _ path: String) throws -> String {
|
||||
try FrontmatterDocument.parse(fixture.indexText(path)).body
|
||||
}
|
||||
|
||||
/// A `StyleRecents` of this test's own — `StyleRecentsTests`' own pattern (`StyleModelTests.swift`),
|
||||
/// restated here rather than shared across files: the list is a real user preference, and a suite
|
||||
/// that wrote into `UserDefaults.standard` would be editing the developer's own quick-style row.
|
||||
@MainActor
|
||||
private func makeRecents() -> (recents: StyleRecents, teardown: () -> Void) {
|
||||
let name = "CardSessionUndoTests-\(UUID().uuidString)"
|
||||
guard let defaults = UserDefaults(suiteName: name) else {
|
||||
Issue.record("could not create a defaults suite")
|
||||
return (StyleRecents(defaults: .standard), {})
|
||||
}
|
||||
return (StyleRecents(defaults: defaults), { defaults.removePersistentDomain(forName: name) })
|
||||
}
|
||||
|
||||
// MARK: - Routing
|
||||
|
||||
@MainActor
|
||||
@@ -452,6 +465,45 @@ struct CardSessionCloseTests {
|
||||
let document = try FrontmatterDocument.parse(fixture.indexText(cardPath))
|
||||
#expect(document.background.isMissing, "the session's net effect, walked back")
|
||||
}
|
||||
|
||||
/// The sidebar's compact `SymbolPicker` (2026-08-08, replacing the embedded curated grid) writes
|
||||
/// through the same funnel the test above exercises for the background combo —
|
||||
/// `StyleCommand.apply`, exactly as `CardStyleSection.applySymbol`/`applyIconColor` call it — so
|
||||
/// this is that same claim, `icon`/`iconColor` instead of `background`. A control swap that left
|
||||
/// this funnel unrouted would still look correct on screen (the well updates) right up until the
|
||||
/// window closes and the change never reaches board history.
|
||||
@Test("A symbol and tint change made in the window's sidebar joins the session, not the board")
|
||||
func sidebarSymbolPickingIsASessionGesture() async throws {
|
||||
let fixture = try WriterFixture()
|
||||
defer { fixture.tearDown() }
|
||||
_ = try makeCommentBoard(fixture)
|
||||
let window = try makeWindow(fixture)
|
||||
let (recents, teardown) = makeRecents()
|
||||
defer { teardown() }
|
||||
|
||||
StyleCommand.apply(
|
||||
icon: .set("flame"),
|
||||
iconColor: .set("carnation"),
|
||||
to: CardStyleSection.target(forCard: cardID),
|
||||
in: window.store,
|
||||
recents: recents,
|
||||
on: window.window
|
||||
)
|
||||
#expect(window.window.stack.undoActionName == "Restyle Card")
|
||||
#expect(!window.board.canUndo)
|
||||
// A symbol pick is not a background pick — "only `.set` reaches `StyleRecents.record`" is
|
||||
// `StyleCommand.apply`'s rule for `background` alone (`StyleEditor.swift`) — so the picker's
|
||||
// glyph and tint wells never touch the quick-style row.
|
||||
#expect(recents.backgrounds.isEmpty)
|
||||
|
||||
await window.session.endSession()
|
||||
#expect(window.board.undoActionName == coarseStep)
|
||||
|
||||
window.board.undo()
|
||||
let document = try FrontmatterDocument.parse(fixture.indexText(cardPath))
|
||||
#expect(document.icon.isMissing, "the session's net effect, walked back")
|
||||
#expect(document.iconColor.isMissing, "and its tint with it")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Transactional staleness
|
||||
|
||||
Reference in New Issue
Block a user