Skip to content
Source
Sign inSign up
Open History to see the last commit

terminalOutputWriter.ts

56 lines1.7 KB

1import {
2 INITIAL_TERMINAL_OUTPUT_CURSOR,
3 readTerminalOutputUpdate,
4 type TerminalOutputState,
5} from "@t3tools/client-runtime/state/terminal";
6​
7/** One writer per mounted native view; intermediate snapshots can be coalesced safely. */
8export function createTerminalOutputWriter(
9 writeOutput: (data: string, reset: boolean) => Promise<void>,
10) {
11 let cursor = INITIAL_TERMINAL_OUTPUT_CURSOR;
12 let pending: TerminalOutputState | null = null;
13 let draining: Promise<void> | null = null;
14 let disposed = false;
15​
16 async function drain() {
17 let recovering = false;
18 try {
19 while (pending !== null) {
20 if (disposed) return;
21 const output = pending;
22 pending = null;
23 const update = readTerminalOutputUpdate(output, cursor);
24 if (update.type === "none") continue;
25 try {
26 await writeOutput(update.data, update.type === "reset");
27 } catch (error) {
28 if (disposed) return;
29 // A rejected command may have partially applied. Replay the latest retained history
30 // once per drain; a persistent native failure must settle instead of spinning.
31 cursor = INITIAL_TERMINAL_OUTPUT_CURSOR;
32 pending ??= output;
33 if (recovering) throw error;
34 recovering = true;
35 continue;
36 }
37 cursor = update.cursor;
38 }
39 } finally {
40 draining = null;
41 }
42 }
43​
44 return {
45 write(output: TerminalOutputState): Promise<void> {
46 if (disposed) return Promise.resolve();
47 pending = output;
48 draining ??= Promise.resolve().then(drain);
49 return draining;
50 },
51 dispose() {
52 disposed = true;
53 pending = null;
54 },
55 };