Skip to content

M4-24 Keyboard shortcuts and focus order

No Date Ruling Reason
D-M4-24-0 2026-10-25 Built on main after M4-21 and M4-23 landed; the changelog entry and these rulings are fragments. Differences to the brief's names, following the merged code: the in-thread search UI is search/thread_search.dart (a dialog over searchQueryControllerProvider with the channel and the thread, because M4-23 built only its API); SearchHitTile takes an optional onOpen so the dialog closes before the jump; ThreadComposerState.focusInput puts the focus into the composer's text field; the macOS menu bar (keyboard/menu_bar.dart) sits in the builder of MaterialApp.router, because a PlatformMenuBar per shell would exist twice during a route change; the test host openCard takes a wrap for the shortcut layer. D-W-100, D-W-114 (merged code wins over the brief's names).
D-M4-24-1 2026-10-25 The table as data (Design note 1): bindings.dart holds one ShortcutRow per row of the first table of 01 7 with its ShortcutBindings (action, kind, key strokes per platform). describeRow writes a row as 01 7 does (alternatives with " / " or ", or ", an arrow after an arrow with the same modifiers as its direction only, the remark after the keys); bindings_spec_test.dart compares every row with the Markdown table of 01 7 for both platforms, and readme_table_test.dart holds the table of apps/client/README.md equal to it. Kinds: app (the shortcut layer binds the key), history (Esc and "Esc then Tab", handled by the history column while the focus is in it), composer (Enter, Shift+Enter, arrow up, handled by the focused input), pointer (Option+Click, Alt+Click on a message). Brief Design note 1; 01 7.
D-M4-24-2 2026-10-25 Scopes (Design note 2): AppShortcuts binds the app keys above the columns of the shell and resolves each intent through a registry of handlers that the views offer with KeyboardActions (enabled flag, optional focus scope, priority). The handler of the view that holds the focus wins, then the higher priority (an open thread over the main history beside it in D2), then the newest. A handler without a target is disabled, and a disabled or unbound key is not consumed. The layer binds no plain key except F6; Esc stays with the history and with the input. App shortcuts with a modifier also work in a text field, so Option or Alt plus arrows switch the channel from the composer instead of moving the caret by paragraph (01 7: app shortcuts with modifiers work everywhere outside the terminal). The shell holds a focus scope of its own, so when a focused item goes (a closed editor, a click outside a field) the focus falls back below the layer and the keys still arrive. KeyboardScope.passThrough returns "skip remaining handlers" for every key without the platform modifier (Cmd on macOS, Ctrl+Shift on Windows), so those keys go to the platform and the terminal of M4-36 and never to an app shortcut (R-PR-10). The set of actions holds no component action; no_approval_shortcut_test.dart dispatches every binding with the focus on an approval button. Shortcuts work in the main view; the settings and administration screens are screens of their own and keep their own keys. Brief Design note 2; R-PR-04, R-PR-10.
D-M4-24-3 2026-10-25 Column order (Design note 3): every column frame of ColumnLayout is a FocusTraversalGroup with ReadingOrderTraversalPolicy and registers with the shell; F6 and Shift+F6 move to the first focusable item (top first, then in the reading direction) of the next or previous column in the order sidebar, list, history, session, wrapping at the ends; a column that is not built (collapsed, too narrow, no forum) or shows nothing focusable is skipped. The input is part of the history column. Esc in the history marks the thread read (the M4-20 action) and arms Tab: the next Tab focuses the input; any other key disarms it. The focus ring is the one of DsTappable (color.focus.ring, color.focus.ring-on-sidebar in the sidebar). Brief Design note 3; 01 7, 8.
D-M4-24-4 2026-10-25 No collision with a system assignment on macOS or Windows: no binding uses Cmd+H, Cmd+M, Cmd+Q, Alt+F4 or the Windows key, checked by a test; 01 7 stays unchanged. The macOS menu items carry no key equivalent (the shortcut layer alone owns the keys, so an action never runs twice) and the application menu keeps the system items About, Hide, Quit, minimize and zoom with their system keys. "Open hosts and terminal" (Cmd+Shift+T, Ctrl+Shift+T) is in the table and the list with the note "Not available yet" and has no handler until M4-36. Brief fixed requirements 10 and 11; Out of scope.