src/switcher/state/SearchModeResolverSpecs.md
Line coverage:
SearchModeResolver.swift100% · refreshed 2026-05-27 by/coverage-explore
The in-switcher Search feature lets the user filter the window list by typing. SearchModeResolver
is the pure decision kernel for its state machine, extracted from TilesView (same pattern as
SelectionResolver). It decides what should happen; TilesView and ShortcutAction carry out the
AppKit side effects (make the search field first responder, place the caret, refresh the UI, toggle
the Edit menu, call App.cycleSelection).
.off — no search field interaction; the switcher behaves normally..editing — the search field is the first responder and editable; typing filters the list..locked — (Pro) the query is frozen and the field is read-only; focus moves to the selected
tile so arrow keys navigate results while the filter stays put..searchOnRelease style. Search is the session.ProFeature.searchInSwitcher.attemptUse() / lockSearchInSwitcher.attemptUse()
have side effects (consume the free pass, surface the upgrade UI), so the caller evaluates them at
the real attempt moment and passes a Bool in. The gate is checked before the state branches,
so a denied attempt never mutates mode (it returns .proGateBlocked). toggle is gate-free — it just
routes to the enter/disable path, which applies its own gate (mirrors the original delegation)..off: entering editing refreshes the UI only when coming from .off
(enterEditing(refreshUi:) carries the original wasOff flag); re-entering from .locked does not.NSSearchField, which handles select-all/copy/paste/cut natively. AltTab does not intercept those..editing.TilesView as side effects): caret placement, first-responder
changes, forceDoNothingOnRelease, hover clearing, key-repeat-timer stops, the Edit-menu toggle, and
endSearchSession teardown (which unconditionally resets to .off and is distinct from disable).Mirrors SearchModeResolverTests.swift 1:1.
startMode(startInSearch: true) → .editing.startMode(startInSearch: false) → .off..off → .enterEditing..editing → .disable..locked → .enterEditing..off + entitled → enterEditing(refreshUi: true)..locked + entitled → enterEditing(refreshUi: false)..editing + entitled → placeCaretOnly..off + not entitled → proGateBlocked(.search).proGateBlocked(.search)..editing → .exitToOff..locked → .exitToOff..off → .noOp..editing + entitled → .lockResults..locked + entitled → .unlockToEditing..off + entitled → .noOp.proGateBlocked(.lockSearch).proGateBlocked(.lockSearch)..exitSearch..exitSearch..closeSwitcher..closeSwitcher..closeSwitcher..closeSwitcher.cycleSelection(dir)..handled..passToShortcuts..passToField (NSSearchField handles editing natively)..passToField even for an arrow..passToField even for Tab..passToField even for a matching shortcut..editing (off/locked are read-only).