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
The board's live title+body filter per 04-interactions.md § Search:
- SearchFilter — a pure value folding the query once (case- and
diacritic-insensitive substring, locale-stable); title OR body matches,
attachment filenames never searched; only the literal empty string is
inactive.
- One universe: the filter threads through SelectionGrammar's order lists
as a defaulted parameter, so ranges, Select All, arrow navigation, the
marquee, drop zones, count badges, and the shown trash all read the same
filtered set by construction; lanes are deliberately never filtered out
(an emptied lane keeps its slot with a 0 badge). Hidden cards leave the
selection through the existing constrain primitive, run on every query
change and as the last line of the reload resolve; the delete successor
is filtered so ⌫ never selects a hidden neighbour.
- The field: an NSSearchField-backed toolbar item (the toolbar's sole
default item); Edit ▸ Find ⌘F focuses it through a focused-value
presentation; stock field-editor dispatch — Return swallowed, Tab is the
keep-filter path to the board, board commands stay enabled except the
caret-chord pair, now one shared caretChordsYield expression.
- Escape is staged: clear the non-empty query (focus stays), hand an empty
field back to the board, clear an active search from board focus —
before Escape's clear-selection meaning.
- Creating a card clears the search (the placeholder funnel); a rename
deliberately gets no carve-out; filter reflow rides the content spring
keyed narrowly on the query.
903 unit tests (24 new).
Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY