Align terminal stream with snapshot-only attach

This commit is contained in:
Mohamed Boudra
2026-03-22 22:49:05 +07:00
parent 138c774297
commit babc724220
14 changed files with 262 additions and 550 deletions
@@ -438,6 +438,31 @@ describe("Terminal", () => {
unsubscribe();
});
it("emits output only after getState reflects the new data", async () => {
const session = trackSession(
await createTerminal({
cwd: "/tmp",
shell: "/bin/sh",
env: { PS1: "$ " },
}),
);
await waitForLines(session, ["$"]);
const outputSeenInState = new Promise<boolean>((resolve) => {
const unsubscribe = session.subscribe((message) => {
if (message.type !== "output" || !message.data.includes("state-after-output")) {
return;
}
unsubscribe();
const stateText = getLines(session.getState()).join("\n");
resolve(stateText.includes("state-after-output"));
});
});
session.send({ type: "input", data: "echo state-after-output\r" });
expect(await outputSeenInState).toBe(true);
});
it("unsubscribe stops receiving messages", async () => {
const session = trackSession(
await createTerminal({
+9 -5
View File
@@ -322,10 +322,13 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
// Pipe PTY output to terminal emulator
ptyProcess.onData((data) => {
if (killed) return;
for (const listener of listeners) {
listener({ type: "output", data });
}
terminal.write(data, () => {
if (disposed || killed) {
return;
}
for (const listener of listeners) {
listener({ type: "output", data });
}
scheduleStateBroadcast();
});
});
@@ -359,6 +362,7 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
case "resize":
terminal.resize(msg.cols, msg.rows);
ptyProcess.resize(msg.cols, msg.rows);
scheduleStateBroadcast();
break;
case "mouse":
// Mouse events can be sent as escape sequences if terminal supports it
@@ -370,8 +374,8 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
function subscribe(listener: (msg: ServerMessage) => void): () => void {
listeners.add(listener);
queueMicrotask(() => {
if (listeners.has(listener)) {
terminal.write("", () => {
if (!disposed && listeners.has(listener)) {
listener({ type: "snapshot", state: getState() });
}
});