diff --git a/src/apps/officer-web/Screens/Dashboard/Layout/Header/ThemeSwitcher.tsx b/src/apps/officer-web/Screens/Dashboard/Layout/Header/ThemeSwitcher.tsx
index 48a60707..442cf60e 100644
--- a/src/apps/officer-web/Screens/Dashboard/Layout/Header/ThemeSwitcher.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Layout/Header/ThemeSwitcher.tsx
@@ -24,10 +24,10 @@ const {
*
* ── The overlay ──
*
- * `ctrl+shift+space` opens a list in the middle of the screen; `j`/`k` move through it and `l` applies the
- * focused theme to the app BEHIND it. That is the whole idea, and it is why this is a portal over a
- * translucent backdrop rather than a modal dialog: you are meant to watch the application change while
- * you browse, so as little of it as possible should be covered.
+ * `ctrl+shift+↑/↓` opens a list in the middle of the screen; `j`/`k` move through it and `l` applies the
+ * focused theme to the app BEHIND it. That is the whole idea, and it is why this is a portal with no
+ * backdrop rather than a modal dialog: you are meant to watch the application change while you browse,
+ * so nothing of it should be covered.
*
* Ported from the owner's `vimazing`, keys and all.
*/
@@ -156,9 +156,9 @@ export const ThemeSwitcher = () => {
{open &&
createPortal(
setOpen(false)}
>
{
// anywhere — including inside a panel that has its own key handling.
useEffect(() => {
const onKey = (ev: KeyboardEvent) => {
- // `code`, not `key`: shift+space reports `key: ' '` on most layouts and something else on a few, while
+ // `code`, not `key`: ctrl+space reports `key: ' '` on most layouts and something else on a few, while
// the physical key is `Space` everywhere.
//
- // Every other modifier must be ABSENT. `ctrl+shift+space` is the theme browser, and one chord opening
- // both is the bug that took a capture-phase listener to fix on the theme side.
+ // Every other modifier must be ABSENT. `ctrl+shift+space` and `ctrl+alt+space` are the theme
+ // browser, and one chord opening both is the bug that took a capture-phase listener to fix on the
+ // theme side.
+ //
+ // Was shift+space until 2026-08-25: typing fires shift+space constantly (any capitalized word
+ // followed by a space), so the launcher kept popping open mid-sentence. Ctrl+space doesn't happen in
+ // normal typing.
//
// 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.shiftKey && !ev.ctrlKey && !ev.altKey && !ev.metaKey && ev.code === 'Space') {
+ if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey && 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
diff --git a/src/apps/officer-web/locales/en-GB.json b/src/apps/officer-web/locales/en-GB.json
index 54a6e18b..f07249f9 100644
--- a/src/apps/officer-web/locales/en-GB.json
+++ b/src/apps/officer-web/locales/en-GB.json
@@ -478,11 +478,11 @@
"codeEditor": "Code Editor"
},
"theme": {
- "pillTitle": "Theme: {{name}} · ctrl+shift+←/→ cycle · ctrl+shift+space browse · ctrl+shift+f {{starAction}}",
+ "pillTitle": "Theme: {{name}} · ctrl+shift+←/→ cycle · ctrl+shift+↑/↓ browse · ctrl+shift+f {{starAction}}",
"pillLabel": "Theme: {{name}}",
"star": "star",
"unstar": "unstar",
- "cycleHint": "ctrl+shift+←/→ cycle · ctrl+shift+space browse",
+ "cycleHint": "ctrl+shift+←/→ cycle · ctrl+shift+↑/↓ browse",
"everyShortcutHint": "ctrl+? for every shortcut",
"favouritesOnly": "Favourites only ({{count}})",
"favourites": "Favourites ({{count}})",
diff --git a/src/apps/officer-web/locales/es-ES.json b/src/apps/officer-web/locales/es-ES.json
index e4e923a2..dd416d51 100644
--- a/src/apps/officer-web/locales/es-ES.json
+++ b/src/apps/officer-web/locales/es-ES.json
@@ -478,11 +478,11 @@
"codeEditor": "Editor de código"
},
"theme": {
- "pillTitle": "Tema: {{name}} · ctrl+shift+←/→ recorrer · ctrl+shift+space explorar · ctrl+shift+f {{starAction}}",
+ "pillTitle": "Tema: {{name}} · ctrl+shift+←/→ recorrer · ctrl+shift+↑/↓ explorar · ctrl+shift+f {{starAction}}",
"pillLabel": "Tema: {{name}}",
"star": "marcar",
"unstar": "desmarcar",
- "cycleHint": "ctrl+shift+←/→ recorrer · ctrl+shift+space explorar",
+ "cycleHint": "ctrl+shift+←/→ recorrer · ctrl+shift+↑/↓ explorar",
"everyShortcutHint": "ctrl+? para todos los atajos",
"favouritesOnly": "Solo favoritos ({{count}})",
"favourites": "Favoritos ({{count}})",
diff --git a/src/apps/officer-web/locales/fr-FR.json b/src/apps/officer-web/locales/fr-FR.json
index 72449952..8f091e17 100644
--- a/src/apps/officer-web/locales/fr-FR.json
+++ b/src/apps/officer-web/locales/fr-FR.json
@@ -478,11 +478,11 @@
"codeEditor": "Éditeur de code"
},
"theme": {
- "pillTitle": "Thème : {{name}} · ctrl+shift+←/→ parcourir · ctrl+shift+space explorer · ctrl+shift+f {{starAction}}",
+ "pillTitle": "Thème : {{name}} · ctrl+shift+←/→ parcourir · ctrl+shift+↑/↓ explorer · ctrl+shift+f {{starAction}}",
"pillLabel": "Thème : {{name}}",
"star": "marquer",
"unstar": "démarquer",
- "cycleHint": "ctrl+shift+←/→ parcourir · ctrl+shift+space explorer",
+ "cycleHint": "ctrl+shift+←/→ parcourir · ctrl+shift+↑/↓ explorer",
"everyShortcutHint": "ctrl+? pour tous les raccourcis",
"favouritesOnly": "Favoris uniquement ({{count}})",
"favourites": "Favoris ({{count}})",
diff --git a/src/apps/officer-web/locales/pt-PT.json b/src/apps/officer-web/locales/pt-PT.json
index 87790a03..da7535fb 100644
--- a/src/apps/officer-web/locales/pt-PT.json
+++ b/src/apps/officer-web/locales/pt-PT.json
@@ -478,11 +478,11 @@
"codeEditor": "Editor de código"
},
"theme": {
- "pillTitle": "Tema: {{name}} · ctrl+shift+←/→ percorrer · ctrl+shift+space explorar · ctrl+shift+f {{starAction}}",
+ "pillTitle": "Tema: {{name}} · ctrl+shift+←/→ percorrer · ctrl+shift+↑/↓ explorar · ctrl+shift+f {{starAction}}",
"pillLabel": "Tema: {{name}}",
"star": "marcar",
"unstar": "desmarcar",
- "cycleHint": "ctrl+shift+←/→ percorrer · ctrl+shift+space explorar",
+ "cycleHint": "ctrl+shift+←/→ percorrer · ctrl+shift+↑/↓ explorar",
"everyShortcutHint": "ctrl+? para todos os atalhos",
"favouritesOnly": "Apenas favoritos ({{count}})",
"favourites": "Favoritos ({{count}})",
diff --git a/src/apps/officer-web/locales/sv-SE.json b/src/apps/officer-web/locales/sv-SE.json
index a0920077..0bc4748b 100644
--- a/src/apps/officer-web/locales/sv-SE.json
+++ b/src/apps/officer-web/locales/sv-SE.json
@@ -478,11 +478,11 @@
"codeEditor": "Kod-editor"
},
"theme": {
- "pillTitle": "Tema: {{name}} · ctrl+shift+←/→ bläddra · ctrl+shift+space bläddra i galleri · ctrl+shift+f {{starAction}}",
+ "pillTitle": "Tema: {{name}} · ctrl+shift+←/→ bläddra · ctrl+shift+↑/↓ bläddra i galleri · ctrl+shift+f {{starAction}}",
"pillLabel": "Tema: {{name}}",
"star": "stjärnmärk",
"unstar": "avmarkera stjärna",
- "cycleHint": "ctrl+shift+←/→ bläddra · ctrl+shift+space galleri",
+ "cycleHint": "ctrl+shift+←/→ bläddra · ctrl+shift+↑/↓ galleri",
"everyShortcutHint": "ctrl+? för alla kortkommandon",
"favouritesOnly": "Endast favoriter ({{count}})",
"favourites": "Favoriter ({{count}})",
diff --git a/src/apps/officer-web/state/shortcuts.ts b/src/apps/officer-web/state/shortcuts.ts
index e4ea9b1f..0bbf6968 100644
--- a/src/apps/officer-web/state/shortcuts.ts
+++ b/src/apps/officer-web/state/shortcuts.ts
@@ -42,7 +42,7 @@ export const SHORTCUT_SECTIONS: ShortcutSection[] = [
titleKey: 'shortcuts.navigation',
shortcuts: [
{
- keys: 'Shift+Space',
+ keys: 'Ctrl+Space',
descriptionKey: 'shortcuts.spotlight',
implementedIn: 'Layout/Spotlight/Spotlight.tsx',
},
@@ -54,7 +54,7 @@ export const SHORTCUT_SECTIONS: ShortcutSection[] = [
noteKey: 'shortcuts.themesNote',
shortcuts: [
{
- keys: 'Ctrl+Shift+Space',
+ keys: 'Ctrl+Shift+↑ / ↓',
descriptionKey: 'shortcuts.openBrowser',
implementedIn: 'theme/useThemeKeys.ts',
},
diff --git a/src/workspaces/theme/src/useThemeKeys.ts b/src/workspaces/theme/src/useThemeKeys.ts
index eab58346..f1aa0b3d 100644
--- a/src/workspaces/theme/src/useThemeKeys.ts
+++ b/src/workspaces/theme/src/useThemeKeys.ts
@@ -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.