fix(switcher): ⌃+⌘+R leaves the Term tab behind

Merged
GR
10d ago
+64-7

Background the app from the Term tab, press ⌃+⌘+R, and the Quick Switcher came back on Term. ⌃+⌘+T is the shortcut for Term; ⌃+⌘+R is for the switcher, and Term is not the switcher.

Why it happened — two causes, neither a state bug

Settings → General → Default Tab was read once, at app start. src/main.ts reads default-switcher-mode and hands it to createSwitcherWindow(...) on launch. Nothing re-applied it afterwards, so the setting really meant "the tab the window opens on the first time it is created".

The Quick Switcher callback never named a tab. quickSwitcherCallback only showed, hid or focused the window. It sent no tab message at all, so the renderer kept whatever mode it last had. Compare terminalCallback, which explicitly sends switch-to-terminal / check-terminal-and-hide — that is why ⌃+⌘+T always lands on Term, and that part is correct and deliberate.

What it does now

Showing the switcher leaves only the Term tab, for whatever Default Tab names. A window parked on Projects or Sessions keeps its place.

Tab before backgroundingAfter ⌃+⌘+R
ProjectsProjects — unchanged
SessionsSessions — unchanged
TermDefault Tab

This is deliberately not a reset-to-default on every show: that would bounce you out of Sessions every time whenever Default Tab is Projects, to fix a case that only ever involves Term. When Default Tab is itself Terminal, the window stays on Term — the same rule, not an exception, because the handler only moves when the target is a switcher tab.

A second, smaller defect fixed on the way: a switcher window re-created after every window had closed — by the shortcut, or by clicking the Dock icon (app.on('activate')) — called createSwitcherWindow() with no argument, so it carried no #mode= hash. The renderer re-applies Default Tab on mount, so such a window did end on the right tab, but only after briefly showing the Projects fallback. All three creation paths now pass it.

One reader for the setting. Startup, the Quick Switcher and activate all read Default Tab through readDefaultTab(), which catches a rejected read (electron-settings rejects on a malformed file — without this the now-async shortcut would never open the window) and normalises any unknown value to 'projects'.

Shape of the change

Four source files plus CHANGELOG, following the existing switch-to-terminal wiring exactly: a new quick-switcher-leave-terminal message carrying the default tab (src/main.ts), the matching onQuickSwitcherLeaveTerminal bridge (src/preload.ts) and its declaration (src/electron-api.d.ts), and one listener in the renderer (src/switcher-ui.tsx) that returns early unless the current mode is terminal and the target is a switcher tab.

In every branch the message is sent before the window is shown or focused. Main-to-renderer messages arrive in send order and window-focus is sent the same way, so the tab switches before the focus event — which then refreshes Sessions once. The listener therefore does not fetch on its own (it used to, which scanned twice).

Checks

  • tsc --noEmit clean; yarn test 209/209.
  • Not verified by running the app — this is shortcut and window behaviour, so it wants a yarn make and the four cases in the table above (plus: close all windows, then press the shortcut, to exercise the second fix).
  • No test added. What is left after the extraction is a three-line guard inside a React effect, and the repo has no renderer-level test harness; adding one for this would be scaffolding rather than coverage. Happy to add one if the harness is wanted anyway.
  • Duplicate-work scan (CLAUDE.md's file-overlap check) found #119, which touches all four of these files. It does not collide: it is a draft last updated 2026-06-26, its package.json still reads 1.0.74 → 1.0.75 against today's 1.0.92, and its main.ts hunk is an import at line 30 — it never mentions quickSwitcherCallback, createSwitcherWindow, default-switcher-mode, setMode or modeRef. It will need a re-cut against main regardless.
  • Formatting: the four touched files already carry 18, 52, 208 and 1814 prettier/prettier findings respectively and the repo runs no lint gate, so the new lines match their neighbours rather than the formatter. The only non-formatting lint note on the added lines is no-explicit-any on the new preload.ts bridge, which is how every other bridge in that file is typed.

No reviewer requested: this repository has one human contributor, who is also the author.

🤖 Generated with Claude Code


Summary by cubic

Fixes the Quick Switcher returning to the Term tab after ⌃+⌘+R. Default Tab was applied only when a window was created, and the switcher callback never sent a tab message, so a window parked on Term stayed there. Showing the switcher now sends a quick-switcher-leave-terminal message with the default tab; the renderer leaves Term only when the target is Projects or Sessions, so windows on those tabs — or a default of Terminal itself — stay put. This also fixes a window re-created after all windows had closed via the shortcut or the Dock icon, which previously flashed the Projects tab before jumping to the default.

Bug Fixes

  • All creation and show paths — startup, the shortcut, and activate — share one readDefaultTab helper that never throws on a malformed settings file.
  • The activate handler reads Default Tab before the window-count check, so two quick Dock clicks can't each see zero windows and create one.
  • The tab message is sent before show/focus; the renderer listener returns early unless the current mode is Terminal, and skips refetching sessions since the focus event refreshes once.
  • Version bumped to 1.0.93 with a changelog entry.

Summary by CodeRabbit

  • Bug Fixes
    • Opening the Quick Switcher from Terminal now selects the configured default tab when the window is shown, while preserving other tabs.
    • Windows created by the shortcut or Dock now open to the configured default tab.
    • If settings cannot be read, the app falls back to the Projects tab without disabling the shortcut.