Commit Graph
3 Commits
Author SHA1 Message Date
rzen 06ee59e24b Implement toolbar customization for both windows
NSToolbar through the existing HostedWindowController rather than
SwiftUI's toolbar — for reasons that are contract, not taste: 03's
transient-search clause is a decision over the toolbar's current
contents, which NSToolbar publishes and SwiftUI's API cannot answer;
Undo/Redo are the system's nil-target responder-chain actions so the
toolbar items validate exactly as the menu rows do (disabled on base
boards, alive in m8 unchanged); and the search item hosts the real
NSSearchField with explicit first-responder control. Customization is
all system furniture — Customize sheet, drag rearrange, display-mode
popup, overflow, autosaved per window kind. Board default: the search
field alone, trailing; catalog adds New Card, New Lane, Undo, Redo,
Show Trash, every action extracted from its menu command so no second
predicate exists. Card default: the Edit Body / Raw Source toggles and
Add Attachment, mirroring their commands' own predicates live via
observation tracking. Removing the search item keeps the promise —
⌘F surfaces the same field as a transient strip under the title bar,
persisting until the query clears, and an overflowed item that cannot
take the keyboard falls through to the strip too.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
2026-07-28 12:57:42 -04:00
rzen 6dc84176fb 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
2026-07-28 09:59:18 -04:00
rzen cf87b72092 Implement live search filtering
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
2026-07-27 22:49:15 -04:00