File size: 2,795 Bytes
50fe3c9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 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<number, Bundle>());
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<number>();
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 (
<div className="relative h-full w-full">
{terminals.map((t) => {
const tabVisible = t.id === activeId;
return (
<div key={t.id} className="absolute inset-0">
<PaneTreeView
node={t.paneTree}
tabVisible={tabVisible}
activeLeafId={t.activeLeafId}
onFocusLeaf={(leafId) => onFocusLeaf(t.id, leafId)}
getBundle={getBundle}
/>
</div>
);
})}
</div>
);
}
|