Make mobile terminals load faster (#1147)

* Speed up terminal restores on mobile

* Preserve terminal output bytes in the app
This commit is contained in:
Mohamed Boudra
2026-05-23 18:41:10 +08:00
committed by GitHub
parent 84f1dfc8cb
commit 846c9b9da3
32 changed files with 1304 additions and 357 deletions
+10 -1
View File
@@ -67,6 +67,7 @@ import type {
ListTerminalsResponse,
CreateTerminalResponse,
SubscribeTerminalResponse,
SubscribeTerminalRequest,
CloseItemsResponse,
KillTerminalResponse,
CaptureTerminalResponse,
@@ -3770,13 +3771,19 @@ export class DaemonClient {
async subscribeTerminal(
terminalId: string,
requestId?: string,
optionsOrRequestId?:
| { restore?: SubscribeTerminalRequest["restore"]; requestId?: string }
| string,
): Promise<SubscribeTerminalPayload> {
const restore = typeof optionsOrRequestId === "object" ? optionsOrRequestId.restore : undefined;
const requestId =
typeof optionsOrRequestId === "object" ? optionsOrRequestId.requestId : optionsOrRequestId;
const resolvedRequestId = this.createRequestId(requestId);
const message = SessionInboundMessageSchema.parse({
type: "subscribe_terminal_request",
terminalId,
requestId: resolvedRequestId,
...(restore ? { restore } : {}),
});
const payload = await this.sendCorrelatedRequest({
requestId: resolvedRequestId,
@@ -4363,6 +4370,8 @@ export class DaemonClient {
frameKind = "output";
} else if (frame.opcode === TerminalStreamOpcode.Snapshot) {
frameKind = "snapshot";
} else if (frame.opcode === TerminalStreamOpcode.Restore) {
frameKind = "output";
}
this.runtimeMetrics?.recordBinaryFrame(
frameKind,
@@ -0,0 +1,28 @@
import { describe, expect, test } from "vitest";
import { TerminalStreamOpcode } from "../shared/binary-frames/index.js";
import { TerminalStreamRouter, type TerminalStreamEvent } from "./terminal-stream-router.js";
describe("terminal-stream-router", () => {
test("routes restore frames as restore events", () => {
const router = new TerminalStreamRouter();
const events: TerminalStreamEvent[] = [];
const payload = new TextEncoder().encode("restored screen");
router.setSlot("term-1", 7);
router.onEvent((event) => events.push(event));
router.handleFrame({
opcode: TerminalStreamOpcode.Restore,
slot: 7,
payload,
});
expect(events).toEqual([
{
terminalId: "term-1",
type: "restore",
data: payload,
},
]);
});
});
@@ -9,7 +9,8 @@ import type { TerminalInput, TerminalState } from "../shared/messages.js";
export type TerminalStreamEvent =
| { terminalId: string; type: "output"; data: Uint8Array }
| { terminalId: string; type: "snapshot"; state: TerminalState };
| { terminalId: string; type: "snapshot"; state: TerminalState }
| { terminalId: string; type: "restore"; data: Uint8Array };
export class TerminalStreamRouter {
private readonly terminalSlots = new Map<string, number>();
@@ -97,6 +98,15 @@ export class TerminalStreamRouter {
return;
}
if (frame.opcode === TerminalStreamOpcode.Restore) {
this.emit({
terminalId,
type: "restore",
data: frame.payload,
});
return;
}
if (frame.opcode === TerminalStreamOpcode.Snapshot) {
const state = decodeTerminalSnapshotPayload(frame.payload);
if (!state) {