返回 DeepSeek-Reasonix
terminalEvents.ts
根目录 / desktop / frontend / src / lib / terminalEvents.ts
1 import { onTerminalExit, onTerminalOutput, type TerminalExitEvent, type TerminalOutputEvent } from "./bridge";
2
3 const MAX_HISTORY_BYTES = 1024 * 1024;
4
5 type TerminalSink = (data: Uint8Array) => void;
6
7 const sinks = new Map<string, TerminalSink>();
8 const exitListeners = new Set<(event: TerminalExitEvent) => void>();
9 const history = new Map<string, Uint8Array[]>();
10 const historyBytes = new Map<string, number>();
11 let started = false;
12 let stopBridge: (() => void) | null = null;
13
14 function decodeBase64(value: string): Uint8Array {
15 if (typeof atob !== "function") return new Uint8Array();
16 const binary = atob(value);
17 const bytes = new Uint8Array(binary.length);
18 for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
19 return bytes;
20 }
21
22 function deliverOutput(event: TerminalOutputEvent): void {
23 const bytes = decodeBase64(event.data);
24 if (bytes.byteLength === 0) return;
25 const queue = history.get(event.id) ?? [];
26 queue.push(bytes);
27 let total = (historyBytes.get(event.id) ?? 0) + bytes.byteLength;
28 while (total > MAX_HISTORY_BYTES && queue.length > 0) {
29 total -= queue.shift()?.byteLength ?? 0;
30 }
31 history.set(event.id, queue);
32 historyBytes.set(event.id, total);
33 sinks.get(event.id)?.(bytes);
34 }
35
36 function deliverExit(event: TerminalExitEvent): void {
37 if (event.removed) forgetTerminalSession(event.id);
38 exitListeners.forEach((listener) => listener(event));
39 }
40
41 export function startTerminalEventBridge(): () => void {
42 if (!started) {
43 started = true;
44 const stopOutput = onTerminalOutput(deliverOutput);
45 const stopExit = onTerminalExit(deliverExit);
46 stopBridge = () => {
47 stopOutput();
48 stopExit();
49 started = false;
50 stopBridge = null;
51 };
52 }
53 return () => stopBridge?.();
54 }
55
56 export function registerTerminalSink(id: string, sink: TerminalSink): () => void {
57 sinks.set(id, sink);
58 (history.get(id) ?? []).forEach((bytes) => sink(bytes));
59 return () => {
60 if (sinks.get(id) === sink) sinks.delete(id);
61 };
62 }
63
64 export function forgetTerminalSession(id: string): void {
65 history.delete(id);
66 historyBytes.delete(id);
67 }
68
69 export function registerTerminalExitListener(listener: (event: TerminalExitEvent) => void): () => void {
70 exitListeners.add(listener);
71 return () => exitListeners.delete(listener);
72 }
73
74 export function __resetTerminalEventBus(): void {
75 sinks.clear();
76 history.clear();
77 historyBytes.clear();
78 stopBridge?.();
79 }
80
81 export const terminalEventBufferLimit = MAX_HISTORY_BYTES;
82
82 lines TYPESCRIPT