From 6b07aafc67e79dede11ac5137dbe98ded35c546e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 22 Aug 2026 03:31:44 +0000 Subject: [PATCH] spotlight opens on alt-space, and on nothing else MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third chord in a day, and each move was the owner naming a collision on the machine he actually uses: ctrl-space is Brave's address bar, shift-space types a space, alt-space is the window menu — and he rebound that rather than give up the chord. Shift-space was the worst of the three and it is worth saying why, because it read as the safe choice. It is a real key combination inside a text field: it types a space, and people hold shift through one more often than they think while typing capitals. So it had to stand down whenever focus was in an input — which is where focus usually is, the chat composer — and a launcher you have to click out of a field to reach is not a launcher. Alt-space means nothing in a text field, so the isTyping guard stops being the thing holding the feature up and becomes belt-and-braces. Kept anyway: two lines against some layout delivering alt-space as a literal character, which is cheap to prevent and expensive to discover. `altKey` with `ev.code`, not `ev.key` — alt-space produces a different `key` on several layouts and nothing at all on others, while the physical key is `Space` everywhere. No second chord. Two triggers for one thing means two things to collide with, and this one has moved twice. The TODO item's id still says ctrl-space. Ids are stable by design, so it stays; its body now records all three moves and which guard was load-bearing for which. Co-Authored-By: Claude Opus 5 --- TODO.md | 60 +++++++++---------- .../Dashboard/Layout/Spotlight/Spotlight.tsx | 38 ++++++++---- ...-spotlight-style-launcher-on-ctrl-space.md | 16 +++-- 3 files changed, 66 insertions(+), 48 deletions(-) diff --git a/TODO.md b/TODO.md index 0b78169f..f9418d1e 100644 --- a/TODO.md +++ b/TODO.md @@ -22,36 +22,36 @@ the owner's OS user and can never be granted. Indirection there really is accide **85 open, 22 closed**, across 28 themes. -| Theme | Open | Closed | -| ------------------------------------------------------------------------------------------ | ---- | ------ | -| [Affordances](todo/affordances/) | 1 | 0 | -| [Speaking into a session from the backend](todo/agent-delivery/) | 2 | 0 | -| [Backburner](todo/backburner/) | 2 | 0 | -| [Background jobs](todo/background-jobs/) | 2 | 0 | -| [Chat interface](todo/chat/) | 1 | 0 | -| [Code editor](todo/code-editor/) | 1 | 0 | -| [Components — the shared UI layer](todo/components/) | 1 | 0 | -| [File watchers](todo/file-watchers/) | 1 | 0 | -| [Fleet — one command across every server](todo/fleet/) | 5 | 2 | -| [Headscale](todo/headscale/) | 0 | 5 | -| [Infra (alpha)](todo/infra/) | 6 | 0 | -| [Known bugs](todo/known-bugs/) | 4 | 1 | -| [Mobile apps](todo/mobile-apps/) | 3 | 0 | -| [Multi-user](todo/multi-user/) | 18 | 3 | -| [Notifications](todo/notifications/) | 1 | 0 | -| [Plugin ecosystem — before it opens to third parties (2026-08-18)](todo/plugin-ecosystem/) | 3 | 0 | -| [Plugin infrastructure](todo/plugin-infrastructure/) | 2 | 0 | -| [Plugin repos — what writing their READMEs turned up](todo/plugin-repos/) | 10 | 9 | -| [Plugin system — gaps the email extraction found (2026-08-18)](todo/plugin-system/) | 3 | 0 | -| [Prompt library](todo/prompt-library/) | 1 | 0 | -| [Shell — spotlight and the dock](todo/shell/) | 1 | 1 | -| [Sub-agents — agents spawning agents](todo/sub-agents/) | 1 | 1 | -| [Sudo terminal modal](todo/sudo-terminal-modal/) | 1 | 0 | -| [Tooling — the list about the list](todo/tooling/) | 1 | 0 | -| [Transmission](todo/transmission/) | 6 | 0 | -| [URL state — routing, query strings and selection](todo/url-state/) | 4 | 0 | -| [Waiting on the owner to run it](todo/waiting-on-owner/) | 2 | 0 | -| [Workspaces & Panels](todo/workspaces-panels/) | 2 | 0 | +| Theme | Open | Closed | +| --- | --- | --- | +| [Affordances](todo/affordances/) | 1 | 0 | +| [Speaking into a session from the backend](todo/agent-delivery/) | 2 | 0 | +| [Backburner](todo/backburner/) | 2 | 0 | +| [Background jobs](todo/background-jobs/) | 2 | 0 | +| [Chat interface](todo/chat/) | 1 | 0 | +| [Code editor](todo/code-editor/) | 1 | 0 | +| [Components — the shared UI layer](todo/components/) | 1 | 0 | +| [File watchers](todo/file-watchers/) | 1 | 0 | +| [Fleet — one command across every server](todo/fleet/) | 5 | 2 | +| [Headscale](todo/headscale/) | 0 | 5 | +| [Infra (alpha)](todo/infra/) | 6 | 0 | +| [Known bugs](todo/known-bugs/) | 4 | 1 | +| [Mobile apps](todo/mobile-apps/) | 3 | 0 | +| [Multi-user](todo/multi-user/) | 18 | 3 | +| [Notifications](todo/notifications/) | 1 | 0 | +| [Plugin ecosystem — before it opens to third parties (2026-08-18)](todo/plugin-ecosystem/) | 3 | 0 | +| [Plugin infrastructure](todo/plugin-infrastructure/) | 2 | 0 | +| [Plugin repos — what writing their READMEs turned up](todo/plugin-repos/) | 10 | 9 | +| [Plugin system — gaps the email extraction found (2026-08-18)](todo/plugin-system/) | 3 | 0 | +| [Prompt library](todo/prompt-library/) | 1 | 0 | +| [Shell — spotlight and the dock](todo/shell/) | 1 | 1 | +| [Sub-agents — agents spawning agents](todo/sub-agents/) | 1 | 1 | +| [Sudo terminal modal](todo/sudo-terminal-modal/) | 1 | 0 | +| [Tooling — the list about the list](todo/tooling/) | 1 | 0 | +| [Transmission](todo/transmission/) | 6 | 0 | +| [URL state — routing, query strings and selection](todo/url-state/) | 4 | 0 | +| [Waiting on the owner to run it](todo/waiting-on-owner/) | 2 | 0 | +| [Workspaces & Panels](todo/workspaces-panels/) | 2 | 0 | ## Records — no items, kept for the reasoning diff --git a/src/apps/officer-web/Screens/Dashboard/Layout/Spotlight/Spotlight.tsx b/src/apps/officer-web/Screens/Dashboard/Layout/Spotlight/Spotlight.tsx index 36a3fff3..81aeaa0f 100644 --- a/src/apps/officer-web/Screens/Dashboard/Layout/Spotlight/Spotlight.tsx +++ b/src/apps/officer-web/Screens/Dashboard/Layout/Spotlight/Spotlight.tsx @@ -11,17 +11,25 @@ import { useSpotlightItems, type SpotlightItem } from './useSpotlightItems'; // // ── Keyboard first, and the trigger ── // -// `shift-space`. The two obvious chords were both taken: `ctrl-space` focuses the address bar in Brave, -// and `alt-space` is load-bearing for the owner elsewhere. `cmd-k` belongs to the browser on some -// platforms and to text fields on others. +// `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: // -// **Shift-space is a real key combination inside a text field** — it types a space, and people hold shift -// through one more often than they think while typing capitals. So the trigger is IGNORED when focus is -// in an input, a textarea or anything contenteditable, and the space is typed as normal. +// 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 // -// The cost of that is worth stating: focus usually IS in the chat composer, so opening Spotlight from -// there means clicking out first. If that turns out to be the common case, a double-tap of shift is the -// escape hatch — it conflicts with nothing anywhere. +// **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 +// they think while typing capitals. Guarding it meant IGNORING the trigger whenever focus was in an input +// — 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 the guard is now belt-and-braces rather than the thing +// holding the feature together. It stays: alt-space reaching a composer as a literal character on some +// layout is cheap to prevent and expensive to discover. +// +// Nothing else opens it. There is deliberately no second chord — two triggers for one thing means two +// things to collide with, and this one moved twice already. // // Escape closes, arrows move, enter goes. The mouse works, but nothing here is designed around it. @@ -41,9 +49,11 @@ import { useSpotlightItems, type SpotlightItem } from './useSpotlightItems'; /** * Is the user typing into something? * - * `shift-space` types a space in any text field, so the trigger has to stand down there or it eats a - * keystroke people make by accident all day. `isContentEditable` covers the editor and any rich input a - * panel brings with it. + * Kept after the trigger moved to `alt-space`, where it is no longer load-bearing. It was written for + * `shift-space`, which types a space in any text field and so HAD to stand down or it ate a keystroke + * people make by accident all day. Alt-space means nothing in a text field — but a layout that delivers it + * as a literal character exists somewhere, and this is two lines. `isContentEditable` covers the editor + * and any rich input a panel brings with it. */ function isTyping(target: EventTarget | null): boolean { const el = target as HTMLElement | null; @@ -175,7 +185,9 @@ export const Spotlight = () => { // anywhere — including inside a panel that has its own key handling. useEffect(() => { const onKey = (ev: KeyboardEvent) => { - if (ev.shiftKey && ev.code === 'Space' && !isTyping(ev.target)) { + // `altKey` and `code`, not `key`: alt-space produces a different `key` on several layouts (and + // nothing at all on some), while the physical key is `Space` everywhere. + if (ev.altKey && ev.code === 'Space' && !isTyping(ev.target)) { ev.preventDefault(); setOpen((wasOpen) => !wasOpen); return; diff --git a/todo/shell/a-spotlight-style-launcher-on-ctrl-space.md b/todo/shell/a-spotlight-style-launcher-on-ctrl-space.md index b0c7224f..6bfb3a9e 100644 --- a/todo/shell/a-spotlight-style-launcher-on-ctrl-space.md +++ b/todo/shell/a-spotlight-style-launcher-on-ctrl-space.md @@ -1,6 +1,6 @@ --- id: shell/a-spotlight-style-launcher-on-ctrl-space -title: "A Spotlight-style launcher, on `shift-space`" +title: 'A Spotlight-style launcher, on `alt-space`' theme: shell status: done created: 2026-08-20 @@ -18,7 +18,7 @@ him elsewhere, so `ctrl-space` unless that clashes too. --- -**Built 2026-08-21** (`81f32a1f`, retriggered in `a2cca526`). `shift-space` in the end: `ctrl-space` +**Built 2026-08-21** (`81f32a1f`, retriggered in `a2cca526`). **`alt-space`** in the end, after two moves: `ctrl-space` focuses the address bar in Brave and never reached the app. It **indexes the dock** rather than keeping a list of its own — the dock is already the composed, @@ -29,9 +29,15 @@ added on top, hand-listed, which is a known cost written into the file. Matching is subsequence with weighted scoring in its own testable file: `chq` finds Command HQ, `apik` finds API keys, `fi` gives Files rather than Profile. Nine tests, all about ORDER rather than matching. -Stands down inside inputs and textareas, because shift-space types a space there. The consequence is -real and stated: focus is usually in the chat composer, so opening it from there means clicking out -first — double-tap shift is the escape hatch if that bites. +The trigger moved twice, and both moves were the owner naming a real collision: `ctrl-space` is Brave's +address bar, then `shift-space` types a space in every text field. The second was the worse one, because +guarding against it meant standing down whenever focus was in an input — which is where focus usually is, +the chat composer — so the launcher had to be prepared for rather than used. **`alt-space`** has no meaning +in a text field, so the guard is now belt-and-braces rather than the thing holding the feature up. It stays +anyway: two lines against a layout somewhere delivering alt-space as a character. + +The id of this item still says `ctrl-space`. Ids are stable by design — see `todo/README.md` — so it is +left alone. **Still open, and the other half of this item:** pruning the dock, and dock auto-hide with effects. Those live in `shell/dock-effects-and-auto-hide`. Chats, files and actions are not in the index yet — routes