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:
@@ -26,6 +26,8 @@ Lanework is in early development. This list tracks what has actually shipped and
|
||||
|
||||
- **The trash** — deletion is two-stage and Finder-shaped. File ▸ Delete (⌘⌫, or a plain ⌫) tombstones the selection in place — cards or lanes, one bracketed write for the whole batch — and View ▸ Show Trash reveals a trailing quasi-lane where tombstoned items live. It is a pure view: nothing moves on disk, so Put Back is byte-perfect and returns an item to its old position. The column takes exactly one width unit while shown, so showing it re-divides the window rather than resizing it. Its order is fully deterministic (newest deletion first, ties by folder name, an unreadable timestamp sorting oldest) and its ancestor walk is absolute — a tombstoned lane is one entry subsuming everything beneath it, its count naming the cards Put Back would return, so recovering a separately deleted card inside it is deliberately two steps. Drag a row out onto any lane to restore it there. Purging is Delete Immediately (⌥⌘⌫) and Empty Trash… (⇧⌘⌫), each confirmed where the loss is real and named by count; Empty Trash always covers the whole trash, never just what a filter is showing. Nothing edit-shaped — Open, Rename, Style… — applies to a tombstoned selection, and a selection never mixes trashed with live.
|
||||
|
||||
- **Live search** — the board window's toolbar carries one item, a search field (Edit ▸ Find, ⌘F), and typing in it filters the board as you type: a card stays when its title *or* its body contains the query, case- and diacritic-insensitively (so `resume` finds "Résumé"), and everything else animates out under one gentle spring while the survivors reflow. Scope is title and body only — attachment filenames are deliberately not searched. The filter is the single source of truth for what's on the board rather than a highlight over it: the masonry, each lane's count badge, drop zones, the rubber band, ⇧-ranges, Select All, arrow navigation and the ⌥-jumps all read it, and the shown trash filters like any other lane, its card rows and lane rows each by their own text. Nothing invisible stays selected — a card the query hides leaves the selection the moment it goes, and so does one an agent edits out of the match while you search. The field is a control, not an editor: board commands stay live and act on the selection while you type (⌘N included, which clears the search first so a new card is never born invisible), only ⌘←/⌘→ and ⌥⌘←/⌥⌘→ stand down so they stay caret chords, plain ⌫ edits the query while ⌘⌫ still deletes the selection, and Return is swallowed because a live filter has nothing to submit. Tab hands the keyboard to the board with the query intact. Escape steps out one layer per press — a non-empty field clears, an empty one returns focus to the board, and a board-focused Escape under an active search clears it before it means deselect. A rename is deliberately not a carve-out: rename a card out of the match during a search and it animates away exactly as an agent's edit would.
|
||||
|
||||
- **The welcome screen** — branding and two actions on the left, recents on the right: board icon, name, containing folder, and the lane/card counts stamped at last close, newest first. The list never opens a board to build itself, so a huge board or an offline volume costs nothing. Single click selects, double click or Return opens, and a context menu carries Open, Reveal in Finder, and Forget. A board that failed to open or restore says so **on its own row**, in the warning tint, carrying the loader's specifics rather than a modal at launch; a board whose bookmark no longer resolves dims to Unavailable with Open and Reveal off and Forget still live; and a failure naming no known board keeps a list of its own rather than vanishing. File ▸ Open Recent lists the same boards — unavailable ones disabled — with Clear Menu at the bottom, which forgets every record because here the registry *is* the menu.
|
||||
|
||||
- **New Board and Duplicate** — File ▸ New Board… (⌥⌘N) opens a Pages-style template chooser with a mini per-lane preview per template, then a save panel for the location; the new board's frontmatter title is the document name you chose, so the window title and the Finder name start out matching. File ▸ Duplicate (⇧⌘S) forks the frontmost board to a Finder-style "Board copy" sibling (then "copy 2", "copy 3"), preceded by the pending-work flush so the copy misses nothing and with the original left open beside it. The copy is literal: every GUID kept — a whole-board copy is a new identity namespace — tombstoned items carried, strays and timestamps untouched.
|
||||
|
||||
Reference in New Issue
Block a user