mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Fix terminal snapshots reflowing after resize
Terminal snapshots now carry soft-wrap row metadata only to clients that advertise support, so restored output can resize like live output without breaking older clients.
This commit is contained in:
@@ -102,7 +102,7 @@ describe("file transfer binary frames", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects malformed metadata and unknown metadata fields", () => {
|
||||
it("rejects malformed metadata but ignores unknown metadata fields", () => {
|
||||
expect(
|
||||
decodeFileTransferFrame(
|
||||
encodeFileTransferFrame({
|
||||
@@ -135,7 +135,18 @@ describe("file transfer binary frames", () => {
|
||||
new DataView(encoded.buffer).setUint16(2 + requestId.byteLength, json.byteLength);
|
||||
encoded.set(json, 4 + requestId.byteLength);
|
||||
|
||||
expect(decodeFileTransferFrame(encoded)).toBeNull();
|
||||
// Non-strict: the unknown `extra` key is stripped and the frame still decodes.
|
||||
expect(decodeFileTransferFrame(encoded)).toEqual({
|
||||
opcode: FileTransferOpcode.FileBegin,
|
||||
requestId: "req-1",
|
||||
metadata: {
|
||||
mime: "image/png",
|
||||
size: 1,
|
||||
encoding: "binary",
|
||||
modifiedAt: "2026-05-02T00:00:00.000Z",
|
||||
},
|
||||
payload: new Uint8Array(),
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects malformed request id prefixes and frame tails", () => {
|
||||
|
||||
@@ -9,14 +9,12 @@ export const FileTransferOpcode = {
|
||||
|
||||
export type FileTransferOpcode = (typeof FileTransferOpcode)[keyof typeof FileTransferOpcode];
|
||||
|
||||
export const FileBeginMetadataSchema = z
|
||||
.object({
|
||||
mime: z.string().min(1),
|
||||
size: z.number().int().nonnegative(),
|
||||
encoding: z.enum(["utf-8", "binary"]),
|
||||
modifiedAt: z.string(),
|
||||
})
|
||||
.strict();
|
||||
export const FileBeginMetadataSchema = z.object({
|
||||
mime: z.string().min(1),
|
||||
size: z.number().int().nonnegative(),
|
||||
encoding: z.enum(["utf-8", "binary"]),
|
||||
modifiedAt: z.string(),
|
||||
});
|
||||
|
||||
export interface FileBegin {
|
||||
opcode: typeof FileTransferOpcode.FileBegin;
|
||||
|
||||
@@ -91,24 +91,26 @@ describe("terminal binary frames", () => {
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects unknown fields in resize and snapshot payloads", () => {
|
||||
it("ignores unknown fields in resize and snapshot payloads", () => {
|
||||
// Protocol schemas are non-strict: unknown keys are stripped, not rejected, so a
|
||||
// new daemon can add fields without breaking an old client's parse.
|
||||
expect(
|
||||
decodeTerminalResizePayload(
|
||||
new TextEncoder().encode(JSON.stringify({ rows: 24, cols: 80, extra: true })),
|
||||
),
|
||||
).toBeNull();
|
||||
expect(
|
||||
decodeTerminalSnapshotPayload(
|
||||
new TextEncoder().encode(
|
||||
JSON.stringify({
|
||||
rows: 1,
|
||||
cols: 1,
|
||||
grid: [[{ char: "A", extra: true }]],
|
||||
scrollback: [],
|
||||
cursor: { row: 0, col: 1 },
|
||||
}),
|
||||
),
|
||||
).toEqual({ rows: 24, cols: 80 });
|
||||
|
||||
const snapshot = decodeTerminalSnapshotPayload(
|
||||
new TextEncoder().encode(
|
||||
JSON.stringify({
|
||||
rows: 1,
|
||||
cols: 1,
|
||||
grid: [[{ char: "A", extra: true }]],
|
||||
scrollback: [],
|
||||
cursor: { row: 0, col: 1 },
|
||||
}),
|
||||
),
|
||||
).toBeNull();
|
||||
);
|
||||
expect(snapshot?.grid[0]?.[0]).toEqual({ char: "A" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { z } from "zod";
|
||||
import { TerminalStateSchema } from "../messages.js";
|
||||
|
||||
export const TerminalStreamResizeSchema = z
|
||||
.object({
|
||||
rows: z.number().int().positive(),
|
||||
cols: z.number().int().positive(),
|
||||
})
|
||||
.strict();
|
||||
export const TerminalStreamResizeSchema = z.object({
|
||||
rows: z.number().int().positive(),
|
||||
cols: z.number().int().positive(),
|
||||
});
|
||||
|
||||
export const TerminalStreamOpcode = {
|
||||
Output: 0x01,
|
||||
|
||||
Reference in New Issue
Block a user