SPB Git

Repository Public

registry.ts 2.7 KB 66 lines typescript

1/**2 * Registry of live terminal controllers. It deliberately has no runtime dependency on xterm so3 * that light components (toolbar, switcher, tabs) can talk to terminals without loading it.4 * Controllers outlive React views: a terminal keeps its WebSocket and buffer while its tab is5 * hidden or while another page is shown, and is re-parented into the view when it comes back.6 */7import type { TerminalController } from './controller';89const controllers = new Map<string, TerminalController>();1011export const getController = (id: string | null | undefined): TerminalController | undefined => (id ? controllers.get(id) : undefined);12export const allControllers = (): TerminalController[] => [...controllers.values()];13export function registerController(c: TerminalController): void {14  controllers.set(c.id, c);15  installGlobalListeners();16}17export function disposeController(id: string): void {18  controllers.get(id)?.dispose();19  controllers.delete(id);20}21export function disposeAllControllers(): void {22  for (const id of [...controllers.keys()]) disposeController(id);23}2425let installed = false;26function installGlobalListeners(): void {27  if (installed || typeof window === 'undefined') return;28  installed = true;29  const wake = () => {30    if (document.visibilityState !== 'visible') return;31    for (const c of controllers.values()) c.wake();32  };33  document.addEventListener('visibilitychange', wake);34  window.addEventListener('online', wake);35  window.addEventListener('pageshow', wake);36  window.addEventListener('offline', () => {37    for (const c of controllers.values()) c.wentOffline();38  });39  window.addEventListener('orientationchange', () => {40    setTimeout(() => {41      for (const c of controllers.values()) c.fit();42    }, 250);43  });4445  if (process.env.NEXT_PUBLIC_E2E === '1') {46    // Test hook (Playwright). Compiled out of production builds.47    (window as unknown as { __crTerm: unknown }).__crTerm = {48      ids: () => [...controllers.keys()],49      text: (id: string) => controllers.get(id)?.bufferText() ?? null,50      status: (id: string) => controllers.get(id)?.status ?? null,51      send: (id: string, data: string) => controllers.get(id)?.send(data),52      dropSocket: (id: string) => controllers.get(id)?.debugDropSocket(),53      sent: (id: string) => controllers.get(id)?.debugSent ?? null,54      size: (id: string) => {55        const c = controllers.get(id);56        return c ? { cols: c.term.cols, rows: c.term.rows, fontSize: c.term.options.fontSize } : null;57      },58      hasFocus: (id: string) => !!controllers.get(id)?.el.contains(document.activeElement),59      scroll: (id: string) => {60        const b = controllers.get(id)?.term.buffer.active;61        return b ? { viewportY: b.viewportY, baseY: b.baseY, type: b.type } : null;62      },63    };64  }65}66
Files main
Menu

Theme