@monotykamary/localterm-server 2.38.1 → 2.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import type { Terminal } from "@xterm/headless";
2
+ export declare const serializeViewport: (terminal: Terminal) => string;
3
+ //# sourceMappingURL=serialize-viewport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize-viewport.d.ts","sourceRoot":"","sources":["../../src/utils/serialize-viewport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAwE7D,eAAO,MAAM,iBAAiB,GAAI,UAAU,QAAQ,KAAG,MAoCtD,CAAC"}
@@ -0,0 +1,119 @@
1
+ // Serialize a headless terminal's visible viewport to an ANSI byte stream
2
+ // that, when written to a viewer's xterm, reproduces the viewport cells
3
+ // (colors, attributes, wide chars) and repositions the cursor — a faithful
4
+ // ~rows×cols "screenshot" the client paints in one write. This is the
5
+ // render-skip payload: when the uplink can't keep up with a megabyte TUI
6
+ // redraw, the server sends this compact viewport snapshot instead of the
7
+ // raw burst, so the viewer sees the latest state in one paint instead of a
8
+ // top-to-bottom crawl.
9
+ //
10
+ // The output is `ESC[2J` (clear the visible screen; the scrollback above is
11
+ // untouched) then one cursor-positioned row of cells at a time, emitting SGR
12
+ // only when a cell's attributes change from the previous one (per-span, not
13
+ // per-cell), then the cursor moved to its viewport position. SGR state is
14
+ // reset before the first differing cell so the snapshot is independent of
15
+ // whatever the viewer's terminal carried from prior output. Cursor visibility
16
+ // and alt-screen entry are intentionally NOT set here — the viewer already
17
+ // tracks those from the live mode-set sequences, so restating them would risk
18
+ // desyncing from the PTY's actual mode state.
19
+ const CSI = "\x1b[";
20
+ const positionCursor = (row, col) => `${CSI}${row};${col}H`;
21
+ // Build the SGR parameter list for a cell's foreground, background, and
22
+ // attributes (bold/dim/italic/underline/blink/inverse/invisible/strikethrough/
23
+ // overline). Palette colors 0-15 use the compact 16-color SGR (30-37 / 90-97
24
+ // for fg, 40-47 / 100-107 for bg); 16-255 use `38;5;n` / `48;5;n`; RGB uses
25
+ // `38;2;r;g;b` / `48;2;r;g;b`. Default fg/bg are 39 / 49.
26
+ const buildSgrParams = (cell) => {
27
+ const params = [];
28
+ if (cell.isBold())
29
+ params.push("1");
30
+ if (cell.isDim())
31
+ params.push("2");
32
+ if (cell.isItalic())
33
+ params.push("3");
34
+ if (cell.isUnderline())
35
+ params.push("4");
36
+ if (cell.isBlink())
37
+ params.push("5");
38
+ if (cell.isInverse())
39
+ params.push("7");
40
+ if (cell.isInvisible())
41
+ params.push("8");
42
+ if (cell.isStrikethrough())
43
+ params.push("9");
44
+ if (cell.isOverline())
45
+ params.push("53");
46
+ if (cell.isFgDefault()) {
47
+ params.push("39");
48
+ }
49
+ else if (cell.isFgPalette()) {
50
+ const color = cell.getFgColor();
51
+ if (color < 8)
52
+ params.push(String(30 + color));
53
+ else if (color < 16)
54
+ params.push(String(90 + (color - 8)));
55
+ else
56
+ params.push(`38;5;${color}`);
57
+ }
58
+ else if (cell.isFgRGB()) {
59
+ const color = cell.getFgColor();
60
+ params.push(`38;2;${(color >> 16) & 0xff};${(color >> 8) & 0xff};${color & 0xff}`);
61
+ }
62
+ if (cell.isBgDefault()) {
63
+ params.push("49");
64
+ }
65
+ else if (cell.isBgPalette()) {
66
+ const color = cell.getBgColor();
67
+ if (color < 8)
68
+ params.push(String(40 + color));
69
+ else if (color < 16)
70
+ params.push(String(100 + (color - 8)));
71
+ else
72
+ params.push(`48;5;${color}`);
73
+ }
74
+ else if (cell.isBgRGB()) {
75
+ const color = cell.getBgColor();
76
+ params.push(`48;2;${(color >> 16) & 0xff};${(color >> 8) & 0xff};${color & 0xff}`);
77
+ }
78
+ return params;
79
+ };
80
+ // `line.getCell(x)` without a reusable cell allocates per call; for a one-shot
81
+ // snapshot on a backpressure burst (not the hot output path) that's fine, and
82
+ // it keeps the read loop free of cell-lifecycle plumbing.
83
+ export const serializeViewport = (terminal) => {
84
+ const buffer = terminal.buffer.active;
85
+ const rows = terminal.rows;
86
+ const cols = terminal.cols;
87
+ const top = buffer.baseY;
88
+ const parts = [`${CSI}2J`, `${CSI}0m`];
89
+ let prev = null;
90
+ for (let row = 0; row < rows; row += 1) {
91
+ const line = buffer.getLine(top + row);
92
+ parts.push(positionCursor(row + 1, 1));
93
+ if (!line) {
94
+ prev = null;
95
+ continue;
96
+ }
97
+ for (let col = 0; col < cols; col += 1) {
98
+ const cell = line.getCell(col);
99
+ if (!cell)
100
+ continue;
101
+ if (cell.getWidth() === 0)
102
+ continue; // wide-char spacer — cursor already past it
103
+ if (prev === null || !cell.attributesEquals(prev)) {
104
+ const params = buildSgrParams(cell);
105
+ parts.push(params.length === 0 ? `${CSI}0m` : `${CSI}0;${params.join(";")}m`);
106
+ }
107
+ const chars = cell.getChars();
108
+ parts.push(chars.length === 0 ? " " : chars);
109
+ prev = cell;
110
+ }
111
+ }
112
+ parts.push(`${CSI}0m`);
113
+ // cursorY is viewport-relative (0..rows-1); cursorX is 0..cols. ANSI is 1-based.
114
+ const cursorRow = buffer.cursorY + 1;
115
+ const cursorCol = Math.min(buffer.cursorX, cols) + 1;
116
+ parts.push(positionCursor(cursorRow, cursorCol));
117
+ return parts.join("");
118
+ };
119
+ //# sourceMappingURL=serialize-viewport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize-viewport.js","sourceRoot":"","sources":["../../src/utils/serialize-viewport.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monotykamary/localterm-server",
3
- "version": "2.38.1",
3
+ "version": "2.39.0",
4
4
  "description": "PTY-backed HTTP/WebSocket server that powers localterm, built on Hono + node-pty.",
5
5
  "keywords": [
6
6
  "hono",