// Opening and closing the bar (docs/COMMAND-BAR.md, "Opening"). `ctrl-k` is a // registry command like any other, so the `?` overlay lists it without a // second list; the touch entry point — the sidebar's search field — raises // `OPEN_COMMAND_BAR_EVENT` and lands on the same open. import { useCallback, useEffect, useState } from "react"; import { OPEN_COMMAND_BAR_EVENT } from "../../catalog.js"; import { CommandSet, defineCommands, isBarTypingTarget } from "../../commands.js"; export interface UseCommandBarOptions { /** Run before the bar opens — the shell discards a preview here, since the * bar is not previewed into (docs/PREVIEW.md). */ onOpen?(): void; /** Off while another overlay owns the keyboard (the restart overlay, the * help dialog). Defaults to on. */ enabled?: boolean; } export interface CommandBarControls { open: boolean; openBar(): void; closeBar(): void; } export function useCommandBar(opts: UseCommandBarOptions = {}): CommandBarControls { const { onOpen, enabled = true } = opts; const [open, setOpen] = useState(false); const openBar = useCallback(() => { onOpen?.(); setOpen(true); }, [onOpen]); const closeBar = useCallback(() => setOpen(false), []); useEffect(() => { if (!enabled) return; const keys = new CommandSet(defineCommands({ "bar.open": openBar })); // Capture phase: an attached xterm swallows every keystroke it is given, so // the bar's binding has to be seen ahead of the terminal. const onKeydown = (e: KeyboardEvent) => { // The bar owns the keyboard while it is up; its own input handles the keys. if (open) return; // The pane's hidden textarea is not a typing target for the bar: ctrl-k // is seen ahead of xterm and opens the bar. A real form field — the spawn // form's inputs — keeps its own Ctrl+K. if (isBarTypingTarget(e.target)) return; keys.handle(e); }; const onTouchEntry = () => { if (!open) openBar(); }; window.addEventListener("keydown", onKeydown, true); document.addEventListener(OPEN_COMMAND_BAR_EVENT, onTouchEntry); return () => { window.removeEventListener("keydown", onKeydown, true); document.removeEventListener(OPEN_COMMAND_BAR_EVENT, onTouchEntry); }; }, [enabled, open, openBar]); return { open, openBar, closeBar }; }