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 2c5b9dc4..1de24ef9 100644 --- a/src/apps/officer-web/Screens/Dashboard/Layout/Spotlight/Spotlight.tsx +++ b/src/apps/officer-web/Screens/Dashboard/Layout/Spotlight/Spotlight.tsx @@ -11,14 +11,35 @@ import { useSpotlightItems, type SpotlightItem } from './useSpotlightItems'; // // ── Keyboard first, and the trigger ── // -// `ctrl-space`. `alt-space` is the more macOS-shaped chord and is already load-bearing for the owner -// elsewhere, so it is deliberately not taken. `cmd-k` is not used either: it belongs to the browser's own -// surface on some platforms and to text fields on others. +// `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. +// +// **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. +// +// 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. // // Escape closes, arrows move, enter goes. The mouse works, but nothing here is designed around it. const MAX_RESULTS = 8; +/** + * 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. + */ +function isTyping(target: EventTarget | null): boolean { + const el = target as HTMLElement | null; + if (!el || !el.tagName) return false; + return el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.isContentEditable === true; +} + /** Label with the matched characters picked out, so it is obvious WHY a result is here. */ const Highlighted = ({ label, hits }: { label: string; hits: number[] }) => { if (!hits.length) return <>{label}; @@ -115,7 +136,7 @@ export const Spotlight = () => { // anywhere — including inside a panel that has its own key handling. useEffect(() => { const onKey = (ev: KeyboardEvent) => { - if (ev.ctrlKey && ev.code === 'Space') { + if (ev.shiftKey && ev.code === 'Space' && !isTyping(ev.target)) { ev.preventDefault(); setOpen((wasOpen) => !wasOpen); return;