Make mobile terminals load faster (#1147)
* Speed up terminal restores on mobile * Preserve terminal output bytes in the app
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user