import type { Tab } from "@/modules/tabs"; import type { SearchAddon } from "@xterm/addon-search"; import { useEffect, useRef } from "react"; import { PaneTreeView } from "./PaneTreeView"; import type { TerminalPaneHandle } from "./TerminalPane"; import { leafIds } from "./lib/panes"; type Props = { tabs: Tab[]; activeId: number; /** Register/unregister handle by leaf id (not tab id). */ registerHandle: (leafId: number, handle: TerminalPaneHandle | null) => void; onSearchReady: (leafId: number, addon: SearchAddon) => void; onCwd: (leafId: number, cwd: string) => void; onExit: (leafId: number, code: number) => void; onFocusLeaf: (tabId: number, leafId: number) => void; }; type Bundle = { setRef: (h: TerminalPaneHandle | null) => void; onSearch: (addon: SearchAddon) => void; onCwd: (cwd: string) => void; onExit: (code: number) => void; }; export function TerminalStack({ tabs, activeId, registerHandle, onSearchReady, onCwd, onExit, onFocusLeaf, }: Props) { const terminals = tabs.filter((t) => t.kind === "terminal"); const registerRef = useRef(registerHandle); const searchReadyRef = useRef(onSearchReady); const cwdRef = useRef(onCwd); const exitRef = useRef(onExit); useEffect(() => { registerRef.current = registerHandle; }, [registerHandle]); useEffect(() => { searchReadyRef.current = onSearchReady; }, [onSearchReady]); useEffect(() => { cwdRef.current = onCwd; }, [onCwd]); useEffect(() => { exitRef.current = onExit; }, [onExit]); const bundles = useRef(new Map()); const getBundle = (leafId: number): Bundle => { let b = bundles.current.get(leafId); if (!b) { b = { setRef: (h) => registerRef.current(leafId, h), onSearch: (addon) => searchReadyRef.current(leafId, addon), onCwd: (cwd) => cwdRef.current(leafId, cwd), onExit: (code) => exitRef.current(leafId, code), }; bundles.current.set(leafId, b); } return b; }; useEffect(() => { const live = new Set(); for (const t of terminals) for (const id of leafIds(t.paneTree)) live.add(id); for (const id of bundles.current.keys()) { if (!live.has(id)) bundles.current.delete(id); } }, [terminals]); return (
{terminals.map((t) => { const tabVisible = t.id === activeId; return (
onFocusLeaf(t.id, leafId)} getBundle={getBundle} />
); })}
); }