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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user