/** * Solid hooks for consuming terminal input events. * * Uses Solid context to make key/mouse/paste events available * to any component in the tree. The Screen wires up the provider. */ import { createContext, createSignal, onCleanup, useContext } from "solid-js"; import type { ParsedKey } from "./parse-keypress.ts"; import type { RawMouseEvent } from "./parse-mouse.ts"; // --- Event bus (signal-based, no EventEmitter overhead) --- export type KeyHandler = (key: ParsedKey) => void; export type MouseHandler = (event: RawMouseEvent) => void; export type PasteHandler = (text: string) => void; export interface InputBus { addKeyHandler(handler: KeyHandler): () => void; addMouseHandler(handler: MouseHandler): () => void; addPasteHandler(handler: PasteHandler): () => void; /** Signal that increments on each key event — for triggering reactive reads */ keySignal: () => number; /** The most recent key event */ lastKey: () => ParsedKey | null; } /** Internal dispatch methods — not exposed to components */ export interface InputBusInternal extends InputBus { _dispatchKey(key: ParsedKey): void; _dispatchMouse(event: RawMouseEvent): void; _dispatchPaste(text: string): void; } export function createInputBus(): InputBusInternal { const keyHandlers = new Set(); const mouseHandlers = new Set(); const pasteHandlers = new Set(); const [keySignal, setKeySignal] = createSignal(0); const [lastKey, setLastKey] = createSignal(null); return { keySignal, lastKey, addKeyHandler(handler: KeyHandler) { keyHandlers.add(handler); return () => { keyHandlers.delete(handler); }; }, addMouseHandler(handler: MouseHandler) { mouseHandlers.add(handler); return () => { mouseHandlers.delete(handler); }; }, addPasteHandler(handler: PasteHandler) { pasteHandlers.add(handler); return () => { pasteHandlers.delete(handler); }; }, _dispatchKey(key: ParsedKey) { setLastKey(key); setKeySignal((n) => n + 1); for (const h of keyHandlers) h(key); }, _dispatchMouse(event: RawMouseEvent) { for (const h of mouseHandlers) h(event); }, _dispatchPaste(text: string) { for (const h of pasteHandlers) h(text); }, }; } // --- Solid Context --- // In Solid 2.0, createContext returns the Provider component directly. // Use it as: {children} export const InputContext = createContext(); // --- Hooks --- function getInputBus(): InputBus { const bus = useContext(InputContext); if (!bus) throw new Error("useInput() must be used inside a Screen"); return bus; } /** * Subscribe to key events. The handler is called for every keypress. * Automatically cleaned up when the component unmounts. */ export function useInput(handler: KeyHandler): void { const bus = getInputBus(); const remove = bus.addKeyHandler(handler); onCleanup(remove); } /** * Subscribe to mouse events. * Automatically cleaned up when the component unmounts. */ export function useMouse(handler: MouseHandler): void { const bus = getInputBus(); const remove = bus.addMouseHandler(handler); onCleanup(remove); } /** * Subscribe to paste events (decoded text). * Automatically cleaned up when the component unmounts. */ export function usePaste(handler: PasteHandler): void { const bus = getInputBus(); const remove = bus.addPasteHandler(handler); onCleanup(remove); }