one modifier, and arrows beside the vim keys
Standardised on `ctrl+shift` for all three global bindings, and added arrows everywhere the vim keys work — `h`/`←`, `l`/`→`, `j`/`↓`, `k`/`↑` are now interchangeable. ctrl+shift+→ or ctrl+shift+l next theme ctrl+shift+← or ctrl+shift+h previous theme ctrl+shift+space the list ── Why ctrl+shift and not the alternatives ── Checked GNOME's actual defaults rather than reasoning from convention: <Control><Alt>Left/Right switch workspace <Control><Shift><Alt>Left/Right move window to workspace <Super><Alt>Left/Right switch workspace So ctrl+alt is unusable — those arrows change your workspace, and `ctrl+alt+l` locks the screen, which is presumably why the original bindings put the letters on ctrl+shift in the first place. `shift+alt` is free on Linux but is the keyboard-layout switcher on Windows and `alt+space` opens the window menu. `ctrl+shift` has no OS binding at all. Its one collision is select-by-word inside a text field — which is now explicitly protected. ── The guard moved earlier ── Typing was only checked once the list was OPEN. With arrows on the global cycle that was no longer enough: `ctrl+shift+←/→` is select-by-word in every text field, and stealing it mid-sentence to change a colour scheme would be indefensible. The check now runs before the global cycle too. Verified against a focused textarea: theme unchanged, and the word selection still happened. The first run of that test reported a hijack, and the test was wrong — it clicked a selector that matched nothing, swallowed the failure, and read body-focus as a text field. `ctrl+alt+space` still opens the list. It shipped an hour ago and fingers learn faster than they unlearn. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,16 @@ import type { ThemeId } from './themes';
|
||||
*
|
||||
* Two modes, and the second is the good one:
|
||||
*
|
||||
* ctrl+shift+l / ctrl+shift+h cycle forward / back, from anywhere, no UI
|
||||
* ctrl+alt+space open the list, then browse it with j/k
|
||||
* ctrl+shift+l / ctrl+shift+→ next theme, from anywhere, no UI
|
||||
* ctrl+shift+h / ctrl+shift+← previous theme
|
||||
* ctrl+shift+space open the list, then browse it
|
||||
*
|
||||
* One modifier for all three, which took some choosing. `ctrl+alt+arrows` switches WORKSPACE on GNOME and
|
||||
* `ctrl+alt+l` locks the screen, so ctrl+alt is unusable for this. `shift+alt` is free on Linux but is the
|
||||
* keyboard-layout switcher on Windows. `ctrl+shift` has no OS binding at all; its only collision is
|
||||
* word-selection inside a text field, which the guard below already prevents.
|
||||
*
|
||||
* Vim keys and arrows are interchangeable everywhere: `h`/`←`, `l`/`→`, `j`/`↓`, `k`/`↑`.
|
||||
*
|
||||
* ── Why browsing applies the theme ──
|
||||
*
|
||||
@@ -48,8 +56,9 @@ export function useThemeKeys() {
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
const { themeId: current, focused: focus, open: isOpen } = state.current;
|
||||
|
||||
// Toggle the list.
|
||||
if (event.ctrlKey && event.altKey && event.code === 'Space') {
|
||||
// Toggle the list. `ctrl+alt+space` still works — it is what this shipped with an hour ago and
|
||||
// fingers are quicker to learn than to unlearn.
|
||||
if (event.ctrlKey && (event.shiftKey || event.altKey) && event.code === 'Space') {
|
||||
event.preventDefault();
|
||||
setOpen((was) => {
|
||||
if (!was) {
|
||||
@@ -61,23 +70,29 @@ export function useThemeKeys() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cycle from anywhere, list or no list.
|
||||
if (event.ctrlKey && event.shiftKey && (event.key === 'L' || event.key === 'l')) {
|
||||
event.preventDefault();
|
||||
setTheme(step(current, 1));
|
||||
return;
|
||||
}
|
||||
if (event.ctrlKey && event.shiftKey && (event.key === 'H' || event.key === 'h')) {
|
||||
event.preventDefault();
|
||||
setTheme(step(current, -1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isOpen) return;
|
||||
|
||||
// Typing in a field should still be typing, even with the list up.
|
||||
/*
|
||||
Typing is typing. Checked BEFORE the global cycle, not just inside the list: `ctrl+shift+←/→` is
|
||||
select-by-word in every text field on earth, and stealing it to change a colour scheme mid-sentence
|
||||
would be indefensible.
|
||||
*/
|
||||
const target = event.target as HTMLElement | null;
|
||||
if (target && (target.isContentEditable || /^(input|textarea|select)$/i.test(target.tagName))) return;
|
||||
const typing = Boolean(target && (target.isContentEditable || /^(input|textarea|select)$/i.test(target.tagName)));
|
||||
|
||||
// Cycle from anywhere, list open or not.
|
||||
if (event.ctrlKey && event.shiftKey && !typing) {
|
||||
if (event.key === 'L' || event.key === 'l' || event.key === 'ArrowRight') {
|
||||
event.preventDefault();
|
||||
setTheme(step(current, 1));
|
||||
return;
|
||||
}
|
||||
if (event.key === 'H' || event.key === 'h' || event.key === 'ArrowLeft') {
|
||||
event.preventDefault();
|
||||
setTheme(step(current, -1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isOpen || typing) return;
|
||||
|
||||
switch (event.key) {
|
||||
case 'j':
|
||||
@@ -94,6 +109,7 @@ export function useThemeKeys() {
|
||||
return;
|
||||
case 'l':
|
||||
case 'L':
|
||||
case 'ArrowRight':
|
||||
case ' ':
|
||||
// Apply what is focused, without closing — the browse.
|
||||
event.preventDefault();
|
||||
@@ -101,6 +117,7 @@ export function useThemeKeys() {
|
||||
return;
|
||||
case 'h':
|
||||
case 'H':
|
||||
case 'ArrowLeft':
|
||||
event.preventDefault();
|
||||
setTheme(startedOn.current);
|
||||
setFocused(startedOn.current);
|
||||
|
||||
Reference in New Issue
Block a user