// Minimal pub/sub for ephemeral UI events that don't belong on the sessionEvents // bus (sessionEvents is for DB-state changes; this file is for UI-only signals // like "user clicked send-to-terminal on selected text"). // // Also exposes a tiny registry of currently-mounted terminal panes so the // MessageBubble context menu can list them. TerminalPane registers on mount, // unregisters on unmount. type Listener = (payload: T) => void; interface EventBus { emit(payload: T): void; subscribe(listener: Listener): () => void; } function createEvent(): EventBus { const listeners = new Set>(); return { emit(payload) { for (const l of listeners) { try { l(payload); } catch { /* one bad listener shouldn't break others */ } } }, subscribe(listener) { listeners.add(listener); return () => { listeners.delete(listener); }; }, }; } export interface SendToTerminalPayload { pane_id: string; text: string; } export const sendToTerminal = createEvent(); export interface TerminalRegistration { paneId: string; label: string; // v1.10.3 kbd-shortcuts: Cmd+` needs to focus the active terminal's xterm // input layer. TerminalPane binds this to term.focus(). focus: () => void; } const terminalRegistry = new Map(); const registryListeners = new Set>(); function notifyRegistry(): void { for (const l of registryListeners) { try { l(); } catch { /* ignore */ } } } export const terminalsRegistry = { register(paneId: string, label: string, focus: () => void): () => void { terminalRegistry.set(paneId, { paneId, label, focus }); notifyRegistry(); return () => { terminalRegistry.delete(paneId); notifyRegistry(); }; }, list(): TerminalRegistration[] { return Array.from(terminalRegistry.values()); }, get(paneId: string): TerminalRegistration | undefined { return terminalRegistry.get(paneId); }, subscribe(listener: Listener): () => void { registryListeners.add(listener); return () => { registryListeners.delete(listener); }; }, };