Skip to content

feat: add a recent windows switcher on mod-tab - #264

Merged
qeude merged 4 commits into
mainfrom
feat/recent-windows-switcher
Nov 11, 2026
Merged

qeude merged 4 commits into
mainfrom
feat/recent-windows-switcher

Conversation

@qeude

@qeude qeude commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

mod-tab opens a recent windows switcher, like Command-Tab but per window and across every workspace and monitor. It follows Niri's next-window / previous-window: the panel appears only after 150 ms, so a brief press switches back without drawing anything.

mod-tab (tap thread)          arm: remember mod's modifiers (not Shift)
  -> recent-windows next      daemon: RecentWindowsSwitcher.step  (no focus change)
Tab / mod-shift-tab           -> next / previous                  (wraps)
release mod (flagsChanged)    -> recent-windows confirm -> focus-window-id <selected>
Escape (while open)           -> recent-windows cancel  (Escape is consumed)
other command, Overview opens,
display change, lock, reload  -> close without choosing
 RuntimeState
-  windowFocusHistory: { current, previous }
+  recentWindowIDs: [WindowID]              # most recent first, persisted in the topology
   previousFocusedWindowTarget              # first live entry after the current one
+  recentWindowCandidates                   # recent first, then the others in workspace order
+RecentWindowsSwitcher (DefiRuntime)        # pure: step, retainManagedWindows, close
+RecentWindowsController (DefiMacOS)        # non-activating panel; WindowCaptureStore picture or app icon
+defiPanelSurface()                         # the cheat sheet's glass and gradient, now shared
  • focus-window-id now switches workspace only when the window's workspace is inactive, like focus-window-previous, so a confirmed switch to a visible window scrolls instead of taking the workspace-switch path. It was only reachable from the CLI before this change.
  • The switcher uses the cheat sheet's surface (Liquid Glass on macOS 26, the same top gradient, 20 pt corners), SwiftUI system fonts, and a .tertiary selection that follows the appearance. Unlike the cheat sheet it does not fade in, like Command-Tab.
  • tab is a new key name and keeps its physical key on every layout, like the navigation keys.
  • Surfaces: CLI and IPC (recent-windows next|previous|confirm|cancel), cheat sheet row (⌃⌥⇥, Shift: backward), Settings action, CONFIGURATION.md, README permissions table, site cheat sheet.
  • Defined cases: refused while the Overview is open; a window that closes leaves the list and the selection moves to the next one; no picture, no Screen Recording, or window_previews = false shows the app icon; the panel shows without animation, so Reduce Motion needs nothing more; holding mod with the switcher open never opens the cheat sheet (a key press already blocks the hold, and any command dismisses it).

Evidence

  • Before: on main, recent-windows is an unknown command, and the history keeps one entry (current and previous).
    After: make verify passes on acd9809d, rebased on main (build, Swift tests, workflow tests). New and extended tests:
    • WindowFocusHistoryTests: MRU order, a closed window skipped, persistence round trip, old stores still decode; the switcher opens on previousFocusedWindowTarget (brief press == focus-window-previous), steps and wraps both ways, and a closed window leaves the list
    • HotKeyTests: releasing the modifier confirms, releasing Shift does not, Escape is taken only while the switcher is open
    • DaemonCommandPolicyTests: focus-window-id activates a workspace only when it is inactive
    • ModelTests, CheatsheetHotKeyTests
    • DaemonRecentWindowsTests: when every listed window closes, the switcher tears down: the Escape capture, the panel and the show task. It failed before the fix.
  • Demo video in the defi-e2e VM: four windows on two workspaces; hold ⌃⌥ with several Tabs and ⇧Tab, then release; two brief presses there and back; Escape. Real VNC keyboard input, Screen Recording granted. It is local to the maintainer's machine: dist/recent-windows-demo.mp4 in this worktree.
  • History survived defi service restart in the VM: focus-window-previous returned to the window focused before the restart.

Not run: the two-display lane, verify.sh desktop, and the Screen Recording-denied path on a real desktop (the icon fallback is in code only).

Merge Danger

Door: two-way

The topology key changes from windowFocusHistory to recentWindowIDs; older stores decode and start with an empty history once. A rollback ignores the new key in the same way.

Blast Radius: keyboard

mod-tab and mod-shift-tab become built-in shortcuts, and every hotkey delivery parses the command to arm the switcher. focus-window-id now scrolls rather than switches for a window that is already visible.

Proof: unit and daemon tests (make verify) plus a VM recording of the installed build with real keyboard input.

Reviewed with review-loop: 2 rounds with claude-opus-5-5 on medium. Verdict: approved

Note

Add a mod-Tab recent windows switcher with daemon, UI, and hotkey support

  • Adds Command.recentWindows with next, previous, confirm, and cancel actions, parsed from CLI as recent-windows <action>, with default bindings mod-Tab and mod-Shift-Tab.
  • RecentWindowsSwitcher tracks window IDs and selection; releasing the held modifier confirms the selected window via focus-window-id, and Escape cancels.
  • RecentWindowsController presents a nonactivating panel with horizontally scrolling window cards, live previews (requires Screen Recording permission), and app icons.
  • RuntimeState.windowFocusHistory is replaced by a recentWindowIDs ordered list. WorkspaceTopology snapshots now store this array, and old stores without it decode with an empty list.
  • Cheatsheet, docs, and site data include the new shortcuts; Tab is now a named physical key via acceleratorKeyKeepsName in hotkey resolution and the shortcut recorder.
  • Behavioral Change: switcher closes on configuration apply, display reconciliation, overview open, session inactive, and shutdown; focus-window-id only reports workspace activation when the target workspace differs from the active one.

Macroscope summarized 12618bb.

@github-actions github-actions Bot added the macroscope-review Request a Macroscope review label Nov 11, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — Adds a recent-window switcher and makes mod-tab and mod-shift-tab built-in shortcuts. Human review is required to accept the new default keyboard behavior.

No code changes detected at 12618bb. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@qeude
qeude force-pushed the feat/recent-windows-switcher branch from 1ff429b to acd9809 Compare November 11, 2026 17:00
Like Command-Tab, but per window and across workspaces and monitors, after
Niri's recent windows switcher. The focus history behind
focus-window-previous becomes a persisted most-recent-first list; the
switcher selects without focusing and confirms through focus-window-id on
mod release, cancels on Escape, and draws nothing for a brief press.
The switcher draws in SwiftUI on the same glass and gradient as the cheat
sheet, with system fonts and a selection that follows the appearance.
Pruning the list first made the switcher read as closed, so the teardown
skipped resetting the Escape capture, the panel and the show task.
@qeude
qeude force-pushed the feat/recent-windows-switcher branch from acd9809 to 12618bb Compare November 11, 2026 17:03
@qeude
qeude merged commit f12ed79 into main Nov 11, 2026
6 checks passed
@qeude
qeude deleted the feat/recent-windows-switcher branch November 11, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Request a Macroscope review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant