spotlight on ctrl+space, theme browser on ctrl+shift+up/down, no backdrop behind the theme list

This commit is contained in:
2026-08-25 16:53:29 +00:00
parent 4a6e4a773c
commit 4efca226f6
9 changed files with 39 additions and 30 deletions
+12 -8
View File
@@ -9,7 +9,7 @@ import type { ThemeId } from './themes';
*
* ctrl+shift+→ next theme, from anywhere, no UI
* ctrl+shift+← previous theme
* ctrl+shift+space open the list, then browse it
* ctrl+shift+↑/↓ open the list, then browse it
* ctrl+shift+f star or unstar the current theme, from anywhere
*
* One modifier for all three, which took some choosing. `ctrl+alt+arrows` switches WORKSPACE on GNOME and
@@ -78,18 +78,22 @@ export function useThemeKeys(options: ThemeKeysOptions = {}) {
const onKeyDown = (event: KeyboardEvent) => {
const { themeId: current, focused: focus, open: isOpen } = state.current;
// Toggle the list. `ctrl+alt+space` is still accepted alongside `ctrl+shift+space` — it is what this
// shipped with first, and fingers are quicker to learn than to unlearn.
if (event.ctrlKey && (event.shiftKey || event.altKey) && event.code === 'Space') {
// Toggle the list. Vertical arrows on the SAME modifier as the horizontal cycle — one family:
// `ctrl+shift+←/→` steps the theme, `ctrl+shift+↑/↓` opens the browse. Was `ctrl+shift+space`
// (with a `ctrl+alt+space` legacy) until 2026-08-25; both space chords are gone, not kept as
// aliases, because `ctrl+space` became Spotlight the same day and space chords near it invite
// the wrong reflex.
if (event.ctrlKey && event.shiftKey && !event.altKey && (event.key === 'ArrowUp' || event.key === 'ArrowDown')) {
event.preventDefault();
/*
Two problems solved by two lines, and both were reported from the same keystroke.
`stopPropagation` stops the header's dropdown ALSO opening. The theme pill is a Radix
DropdownMenuTrigger, and a focused trigger opens on Space — so if the pill happened to have focus,
the chord opened the overlay and the menu behind it. React attaches its handlers at the root, which
bubbles before window, so the trigger saw the key first no matter what this listener did with it.
This listener now runs in the CAPTURE phase and stops the event dead.
DropdownMenuTrigger, and a focused trigger opens on ArrowDown (it did on Space too, when Space
was the chord) — so if the pill happened to have focus, the chord opened the overlay and the
menu behind it. React attaches its handlers at the root, which bubbles before window, so the
trigger saw the key first no matter what this listener did with it. This listener now runs in
the CAPTURE phase and stops the event dead.
The blur stops the overlay opening with focus still in a text field, which made j/k/l do nothing:
the guard below correctly refuses to steal keys from an input, and the input never lost focus.