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
This commit is contained in:
@@ -583,11 +583,17 @@ struct LaneView: View {
|
||||
/// layout that re-admitted them on every flip would flap the board under the cursor
|
||||
/// (DRAG-REORDER.md § Resting-layout zones). The copy's originals reappear when the write lands.
|
||||
///
|
||||
/// This is also the collection m5's search filter narrows, which is what keeps the count badge
|
||||
/// honest for free — see `countBadge`.
|
||||
/// **A card the live search filter hides renders nowhere either** (04-interactions.md § Search):
|
||||
/// "cards whose title *and* body both miss the query animate out". This is the one collection
|
||||
/// that narrowing, which is what makes the filter "the single source of truth for what's on the
|
||||
/// board" true of this lane's every surface at once — the masonry, the count badge (see
|
||||
/// `countBadge`), the drop zones' resting layout, the marquee registration and the Finder
|
||||
/// file-drop targets all read this list or the registry it populates, so none of them needs a
|
||||
/// rule of its own.
|
||||
private var renderedCards: [Card] {
|
||||
let hidden = drops.session.hiddenMembers(onBoardRooted: store.rootURL)
|
||||
return lane.cards.filter { !$0.isDeleted && !hidden.contains($0.id) }
|
||||
let filter = store.searchFilter
|
||||
return lane.cards.filter { !$0.isDeleted && !hidden.contains($0.id) && filter.matches($0) }
|
||||
}
|
||||
|
||||
// MARK: - Selection
|
||||
|
||||
Reference in New Issue
Block a user