Build Preview mode rendering

The card body's resting state: swift-markdown (pinned 0.8.0, smart
typography off — Preview renders the bytes on disk) parsed into a pure
BodyMarkup model with UTF-8 source offsets, rendered on one hosted
TextKit 1 NSTextView — chosen because find-in-text is NSTextFinder,
checkbox clicks reuse AppKit character hit-testing, links are .link
attributes, and NSTextTable's automatic layout is exactly the
columns-sized-to-contents rule. The GFM subset renders per 05; HTML
stays verbatim code-styled text; relative images resolve against the
card folder while remote URLs are never fetched, drawing a quiet chip
instead. Task checkboxes are live: a click flips exactly one byte
through a fresh-read, refuse-uneditable, stamp, atomic-replace write —
the app's only offset-addressed write, so a moved target refuses as
staleTarget and what the user saw decides the direction, netting one
toggle on a double-click. Empty bodies open in Edit per CardBodyMode's
opening rule, applied once; the Edit surface itself stays an honest
read-only stub until its card. FindCommand prefers the card body's
find over board search when a card window is focused.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-28 09:59:18 -04:00
parent 7f1adf47c5
commit 6dc84176fb
15 changed files with 2608 additions and 18 deletions
+62
View File
@@ -189,6 +189,68 @@ struct CardWindowMetricsTests {
}
}
// MARK: - The body column's mode
/// 05-card-window.md Mode grammar, as the two rules a unit test can hold: **Preview is the resting
/// state unless the body is empty**, and **the flip is one flip** whichever key produced it.
///
/// Both are silent failures of exactly the kind this file exists for. A card that opened in Preview
/// with an empty body would look like a working window showing nothing, and the user would have to
/// discover E to write the first word of a card they just made which is the ceremony the rule was
/// settled to remove ("a new card has nothing to preview, so during creation flows title body
/// without a mode stop").
@MainActor
@Suite("Card body mode")
struct CardBodyModeTests {
@Test("A card with a body opens in Preview")
func aCardWithABodyOpensInPreview() {
#expect(CardBodyMode.opening(body: "# Notes\n") == .preview)
#expect(CardBodyMode.opening(body: "x") == .preview)
}
@Test("An empty body opens straight into Edit — whitespace included")
func anEmptyBodyOpensInEdit() {
#expect(CardBodyMode.opening(body: "") == .edit)
#expect(CardBodyMode.opening(body: "\n") == .edit)
// A card the app itself just minted has exactly this body: `BoardWriter.newDocumentText`
// writes frontmatter and nothing after the closing delimiter.
#expect(CardBodyMode.opening(body: " \n\t\n") == .edit)
}
@Test("⌘E, Return in Preview and Escape in Edit are one flip")
func theToggleIsSymmetric() {
#expect(CardBodyMode.preview.toggled == .edit)
#expect(CardBodyMode.edit.toggled == .preview)
#expect(CardBodyMode.preview.toggled.toggled == .preview)
}
@Test("The opening rule runs once, not on every snapshot")
func theOpeningRuleIsAppliedOnce() {
let presentation = CardBodyPresentation()
#expect(presentation.openIfNeeded(body: "# Notes\n") == .preview)
// The user presses E
presentation.toggleMode()
#expect(presentation.mode == .edit)
// and a watcher reload arrives with the same body. Re-deciding here would throw the user
// out of the surface they just asked for.
#expect(presentation.openIfNeeded(body: "# Notes\n") == .edit)
#expect(presentation.mode == .edit)
}
@Test("A window that opened into Edit is not dragged back to Preview when the body fills up")
func aLaterBodyDoesNotReopenTheRule() {
let presentation = CardBodyPresentation()
#expect(presentation.openIfNeeded(body: "") == .edit)
// The user types; the reload brings the text back. The rule is about *opening* a card, and
// this window is already open.
#expect(presentation.openIfNeeded(body: "first words") == .edit)
}
}
// MARK: - Identity and frames
@MainActor