.claude/skills/verify-miri/features/focus-window-shortcut.md
Focus a window with a shortcut
A shortcut moves keyboard focus to the next column left or right in the strip, or to the window above or below in a stacked column. The engine scrolls the strip so the focused column shows, and macOS makes that window's app frontmost.
Sub-features
focus-west: ⌃⌥← (also ⌃⌥H and ⌃⌥A) focuses the column to the left.focus-east: ⌃⌥→ (also ⌃⌥L and ⌃⌥D) focuses the column to the right.focus-north,focus-south: ⌃⌥↑ and ⌃⌥↓ (also ⌃⌥K and ⌃⌥J, ⌃⌥W and ⌃⌥S) focus the window above or below in a stacked column.focus-display: ⌃⌥⌘← and ⌃⌥⌘→ (also ⌃⌥⌘H and ⌃⌥⌘L, ⌃⌥⌘A and ⌃⌥⌘D) focus the monitor on that side from any column in the strip. ⌃⌥⌘↑ and ⌃⌥⌘↓ (also ⌃⌥⌘K and ⌃⌥⌘J, ⌃⌥⌘W and ⌃⌥⌘S) focus the monitor above or below. Each lands on the window last focused on that monitor.focus-edge: Focus first column or monitor left and Focus last column or monitor right remain in Settings with no default shortcut.focus-number: ⌃⌥1 to ⌃⌥9 (Jump to column N) focus a column by its position, on the window last focused in that column.
How to get to it (user POV)
- Press the chord on the keyboard while any app is in front.
- Change the chords in Settings > Shortcuts, or in
[bindings]ofconfig.toml.
Driving it with verify-miri
Preconditions:
The baseline from the index.
config.tomlbindswindow_focus_westtoctrl+alt-leftarrowandwindow_focus_easttoctrl+alt-rightarrow.Open two columns. Run
V fixture --count 2. The log showsfixture-1andfixture-2with window IDs andthe engine manages it. The engine placesfixture-2in a new column to the right offixture-1and focuses it.Record the start. Run
V snapshot 1-before. The log showsengine focus fixture-2, macOS frontmost fixture-2. In the JSON, both fixtures have"floating": false, andfixture-1'sxis smaller.Focus left. Run
V key ctrl+alt-leftarrow, thenV focused --wait-for fixture-1. The command exits 0.Record it. Run
V snapshot 2-after-left. The log showsengine focus fixture-1, macOS frontmost fixture-1.2-after-left-fixture-1.pngshows colored window buttons, and2-after-left-fixture-2.pngshows grey ones.Focus right. Run
V key ctrl+alt-rightarrow,V focused --wait-for fixture-2, andV snapshot 3-after-right. Focus is back onfixture-2in both views.Letter chords. Run
V key ctrl+alt-h,V focused --wait-for fixture-1, thenV key ctrl+alt-l,V focused --wait-for fixture-2. Do the same withctrl+alt-aandctrl+alt-d. Each exits 0, as for the arrows.Column number. Read
fixture-1's position in the active strip fromV query state, wherevirtual_workspaces[].windowslists the strip in order. Press that number, such asV key ctrl+alt-2, thenV focused --wait-for fixture-1.Stack. With
fixture-2focused, runV key ctrl+alt-leftbracket. In the next snapshot both fixtures sharefixture-1'sx, with half the height each.Focus within the stack. Run
V key ctrl+alt-uparrowandV focused --wait-for fixture-1, thenV key ctrl+alt-downarrowandV focused --wait-for fixture-2. Do the same withkandj, and withwands.Unstack. Run
V key ctrl+alt+shift-rightbracket.fixture-2returns to its own column on the right.
In the proof run on 2026-11-05, both views agreed about 0.2 s after each key press.
Direct monitor focus
Monitor focus lands on the window last focused on the destination display. That is one of Sam's windows unless a fixture held focus there last, so set that up first. Check the display arrangement before you pick the arrow: on 2026-11-05 display 1 sat below display 3, so only ⌃⌥⌘↑ and ⌃⌥⌘↓ had a destination.
For displays one above the other:
- Open two fixtures, focus
fixture-2, and move it withV send-cmd window movedisplay southas setup. - Press ⌃⌥⌘↑, then ⌃⌥→ until
fixture-1holds focus, so it is the last focused window on the upper display. - Press ⌃⌥⌘↓ and
V focused --wait-for fixture-2, then ⌃⌥⌘↑ andV focused --wait-for fixture-1. Repeat withctrl+alt+cmd-jand-k, and with-sand-w.
For displays side by side, use at least three fixture columns on each display. Record the fixture IDs and use engine commands only to set up the source's interior column and the destination's active fixture. Confirm that the source fixture has a column on both sides before each shortcut.
- Upgrade a version 4 configuration with the exact default edge-focus arrays.
Check that
window_focusdisplay_westandwindow_focusdisplay_eastnow hold the Command-layer chords, and both edge-focus entries are empty. - Press ⌃⌥⌘→ from an interior column on the left display. Record a snapshot and confirm that both engine focus and macOS focus select the destination fixture on the right display.
- Press ⌃⌥⌘← from an interior column on the right display. Confirm both focus observations on the left display.
- Open Settings > Shortcuts. Confirm that Focus first column or monitor left and Focus last column or monitor right remain visible with no shortcut chips.
- In isolated configuration tests, cover customized, deleted, empty, and conflicting bindings, then load a fresh store to confirm that conflict notices persist. Do not overwrite Sam's custom bindings for a live check.
Gotchas
V fixture --fixedmakes a window that cannot resize. The engine floats it, and column focus skips it, so focus moves between Sam's windows instead. PlainV fixturewindows resize, and the engine tiles them.- From a floating window, such as Miri Settings, ⌃⌥← and ⌃⌥→ move only to another floating window on that side, and do nothing when there is none.
V send-cmd window focus westdoes the same. ⌃⌥1 to ⌃⌥9 still work, so jump to a fixture's column number to leave Settings. - A fixture started from a shell, not through
open, never comes to the front. Focus then stays on Sam's window, and the before snapshot shows no fixture focused. - At the left end of the strip, a fresh ⌃⌥← crosses only if a display touches that side and overlaps it vertically. With no such display it does nothing. At the right end with no such display, ⌃⌥→ enters a fullscreen Space when one exists. Auto-repeat stays within the current strip.
- New windows open on the primary display. Check
engine_active_displayin the snapshot before you compare positions. - The engine reports focus a few hundred milliseconds after the press. Use
--wait-for, not a fixed sleep.