spotlight opens on shift-space, and stands down inside a text field
ctrl-space focuses the address bar in Brave, so it never reached the app. shift-space is a real 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 is worth stating rather than discovering: 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. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user