ctrl+space opens spotlight, and ctrl+? tells you everything else

Fourth chord for the launcher, and the last move is a return: ctrl-space → shift-space → alt-space →
ctrl-space. Every one was the owner naming a real collision, and this one is the interesting direction —
alt-space is the GNOME window menu, he rebound the desktop to keep it, disliked what he rebound it to, and
would rather move the app than the window manager.

ctrl-space was ruled out on 2026-08-20 as "focuses the address bar in Brave". That observation was made
before anything preventDefault'ed it, and the trade has changed: rebinding a browser is cheaper than
rebinding a desktop. Written down in the file, because if the address bar still wins the key never reaches
the handler and nothing in this repository can help — the fix would be in Brave.

`!ev.shiftKey && !ev.altKey` matters: ctrl+SHIFT+space is the theme browser, and one chord opening both is
the bug that needed a capture-phase listener to fix on the theme side. Verified in the built bundle that the
two conditions are disjoint.

CTRL+? renders every shortcut from a registry, ported in shape from vimazing's gameInfo.ts — controls
declared as data, help screen renders them. Its renderer was a recursive `any` walk, right for a game whose
help also carries rules and scoring; here the content is one shape, so the renderer is typed and boring and
the value is in state/shortcuts.ts.

The registry is deliberately NOT authoritative yet, and says so: the handlers still hold their own
conditions, so changing a chord here does not move it. Each entry names the file that implements it, so the
pair is one search apart. Making it the source of truth is the configurable-shortcuts work, and doing it now
would rewrite three handlers for nothing visible.

Also removed a line I put in the theme dropdown yesterday promising "alt+space to search every theme by
name". Themes are not Spotlight items — that was an instruction for a feature nobody built, sitting in the
UI as though it worked. It now points at ctrl+?, which exists. Two comments naming ctrl+alt+space for the
theme browser were corrected to ctrl+shift+space with the older chord noted as still accepted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 15:57:42 +00:00
co-authored by Claude Opus 5
parent bcdc769083
commit f586d748d6
6 changed files with 233 additions and 12 deletions
@@ -11,12 +11,18 @@ import { useSpotlightItems, type SpotlightItem } from './useSpotlightItems';
//
// ── Keyboard first, and the trigger ──
//
// `alt-space`, and ONLY that. It went ctrl-space → shift-space → alt-space, and each move was the owner
// telling us what the chord actually collides with on the machine he uses:
// `ctrl-space`, and ONLY that. It has now been ctrl-space → shift-space → alt-space → ctrl-space, and every
// move was the owner naming a real collision on the machine he uses:
//
// ctrl-space focuses the address bar in Brave
// shift-space types a space — see below, this was the real problem
// alt-space taken by the window menu, and the owner rebound it rather than give up the chord
// shift-space types a space — see below, this was the worst of them
// alt-space the GNOME window menu. He rebound the OS to keep it, disliked what he rebound it TO, and
// asked for the OS binding back — so the app moves rather than the desktop.
// ctrl-space chosen deliberately on the way back. It was ruled out on 2026-08-20 as "focuses the
// address bar in Brave"; that observation was made before anything preventDefault'ed it, and
// the owner would rather rebind a browser than his window manager.
//
// If the address bar still steals it, the key never reaches this handler and nothing here can help — the fix
// is in Brave, not in this file. Worth knowing WHICH of the two it is before changing anything.
//
// **Shift-space was never a good choice**, and that is why it did not last a day. It is a real key
// combination inside a text field: it types a space, and people hold shift through one more often than
@@ -24,7 +30,7 @@ import { useSpotlightItems, type SpotlightItem } from './useSpotlightItems';
// — which is where focus usually is, the chat composer — so opening Spotlight meant clicking out first.
// A launcher you have to prepare for is not a launcher.
//
// Alt-space has no meaning inside a text field, so there is NO focus check at all. The guard shift-space
// Ctrl-space has no meaning inside a text field, so there is NO focus check at all. The guard shift-space
// needed was kept for one commit as "belt-and-braces" and was simply the old bug wearing a new hat: focus
// is usually in the chat composer, so a launcher that stands down there is a launcher that does not open
// when you want it. `preventDefault` on the handler covers the only real risk the guard addressed.
@@ -200,15 +206,18 @@ export const Spotlight = () => {
// anywhere — including inside a panel that has its own key handling.
useEffect(() => {
const onKey = (ev: KeyboardEvent) => {
// `altKey` and `code`, not `key`: alt-space produces a different `key` on several layouts (and
// `ctrlKey` and `code`, not `key`: a modifier+space produces a different `key` on several layouts (and
// nothing at all on some), while the physical key is `Space` everywhere.
//
// `!ev.shiftKey`, because ctrl+SHIFT+space is the theme browser. Without it one chord would open both,
// which is the bug that took a capture-phase listener to fix on the theme side.
//
// No focus check. There was one, inherited from shift-space where it was mandatory, and keeping it
// "as belt-and-braces" was wrong in the plainest way: focus is usually IN the chat composer, so the
// guard meant the launcher did not open from the place it is most often wanted. `preventDefault`
// below covers the only thing the guard was protecting against — a layout that would otherwise type
// a character.
if (ev.altKey && ev.code === 'Space') {
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && ev.code === 'Space') {
ev.preventDefault();
// Take focus off whatever had it, explicitly, before the overlay mounts. The effect below focuses
// this panel's own input, and on paper that is enough — but "on paper" is what produced arrow keys