@cplieger/web-terminal-engine 3.0.0 → 3.0.6
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.
- package/jsr.json +1 -1
- package/package.json +4 -4
- package/src/index.ts +1 -1
- package/src/render.ts +35 -12
- package/src/wire-compatibility.ts +8 -1
package/jsr.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cplieger/web-terminal-engine",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"typecheck": "tsc -p tsconfig.json",
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"esbuild": "0.28.1",
|
|
22
22
|
"eslint": "10.7.0",
|
|
23
23
|
"fast-check": "4.9.0",
|
|
24
|
-
"happy-dom": "20.
|
|
24
|
+
"happy-dom": "20.11.0",
|
|
25
25
|
"pngjs": "7.0.0",
|
|
26
|
-
"prettier": "3.9.
|
|
26
|
+
"prettier": "3.9.6",
|
|
27
27
|
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
28
|
-
"typescript-eslint": "8.
|
|
28
|
+
"typescript-eslint": "8.65.0",
|
|
29
29
|
"vitest": "4.1.10"
|
|
30
30
|
},
|
|
31
31
|
"description": "Browser terminal engine: VT500 screen buffer, DOM renderer, and binary wire protocol",
|
package/src/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ export {
|
|
|
15
15
|
WIRE_INCOMPATIBLE_CLOSE_CODE,
|
|
16
16
|
WIRE_PROTOCOL_VERSION,
|
|
17
17
|
} from "./wire-compatibility.js";
|
|
18
|
-
export type { WireIncompatibility } from "./wire-compatibility.js";
|
|
18
|
+
export type { WireCompatibility, WireIncompatibility } from "./wire-compatibility.js";
|
|
19
19
|
export { controlFrame, CONTROL_FRAME_PREFIX } from "./wire.js";
|
|
20
20
|
export { wsURL } from "./wsurl.js";
|
|
21
21
|
export { WS_PATH, SESSIONS_PATH, SESSION_EVENTS_PATH } from "./routes.js";
|
package/src/render.ts
CHANGED
|
@@ -125,13 +125,17 @@ const rowEls = new Map<number, HTMLDivElement>();
|
|
|
125
125
|
let trimMarkerEl: HTMLDivElement | null = null;
|
|
126
126
|
|
|
127
127
|
// Rows awaiting a DOM (re)build, processed in budgeted batches across frames.
|
|
128
|
-
// A session restore (kiro-cli's /chat)
|
|
129
|
-
// lines in one wire frame; building them all
|
|
130
|
-
// constrained device, hangs the tab. The store
|
|
131
|
-
// at once (it is cheap, pure data); the renderer
|
|
132
|
-
// MAX_ROWS_PER_FRAME per frame and reschedules until
|
|
133
|
-
//
|
|
134
|
-
//
|
|
128
|
+
// A session restore (kiro-cli's /chat), a post-resize transcript reprint, or a
|
|
129
|
+
// `cat bigfile` dumps thousands of lines in one wire frame; building them all
|
|
130
|
+
// in a single rAF janks or, on a constrained device, hangs the tab. The store
|
|
131
|
+
// still ingests the whole burst at once (it is cheap, pure data); the renderer
|
|
132
|
+
// drains this queue at most MAX_ROWS_PER_FRAME per frame and reschedules until
|
|
133
|
+
// it is empty, so each frame stays short. The drain is VIEWPORT-FIRST each
|
|
134
|
+
// frame (see flushRenderInner): live-window rows build before any scrollback
|
|
135
|
+
// backlog, and scrollback drains newest->oldest so the backlog fills upward,
|
|
136
|
+
// above the bottom-pinned viewport — offscreen — instead of visibly streaming
|
|
137
|
+
// through it. The cursor row is always built regardless of the budget so the
|
|
138
|
+
// caret never lags.
|
|
135
139
|
const renderQueue = new Set<number>();
|
|
136
140
|
const MAX_ROWS_PER_FRAME = 300;
|
|
137
141
|
|
|
@@ -779,12 +783,31 @@ function flushRenderInner(): void {
|
|
|
779
783
|
renderQueue.delete(newCursorAbs);
|
|
780
784
|
}
|
|
781
785
|
|
|
782
|
-
// Drain up to MAX_ROWS_PER_FRAME queued rows this frame;
|
|
783
|
-
// to the next frame (scheduled below) so one big burst
|
|
784
|
-
//
|
|
785
|
-
//
|
|
786
|
-
//
|
|
786
|
+
// Drain up to MAX_ROWS_PER_FRAME queued rows this frame, viewport-first;
|
|
787
|
+
// the rest carry over to the next frame (scheduled below) so one big burst
|
|
788
|
+
// never blocks paint. Live-window rows build first (ascending), then the
|
|
789
|
+
// scrollback backlog newest->oldest, so the backlog fills upward above the
|
|
790
|
+
// bottom-pinned viewport — offscreen. A Set's insertion order did the
|
|
791
|
+
// opposite under load: a multi-thousand-row backlog (a resume replay,
|
|
792
|
+
// kiro-cli's post-resize transcript reprint, `cat bigfile`) queued ahead of
|
|
793
|
+
// freshly-dirtied window rows, so the visible screen either churned through
|
|
794
|
+
// history or froze (only the force-built cursor row moving) for seconds on
|
|
795
|
+
// a slow device while the backlog drained. flushDrainedThisPass doubles as
|
|
796
|
+
// the per-frame budget counter and the forward-progress signal the
|
|
797
|
+
// error-path reschedule reads (it was reset to 0 at entry and nothing
|
|
798
|
+
// between there and here touches it).
|
|
799
|
+
const inWindow: number[] = [];
|
|
800
|
+
const belowWindow: number[] = [];
|
|
787
801
|
for (const abs of renderQueue) {
|
|
802
|
+
if (abs >= win.base) {
|
|
803
|
+
inWindow.push(abs);
|
|
804
|
+
} else {
|
|
805
|
+
belowWindow.push(abs);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
inWindow.sort((a, b) => a - b);
|
|
809
|
+
belowWindow.sort((a, b) => b - a);
|
|
810
|
+
for (const abs of [...inWindow, ...belowWindow]) {
|
|
788
811
|
if (flushDrainedThisPass >= MAX_ROWS_PER_FRAME) {
|
|
789
812
|
break;
|
|
790
813
|
}
|
|
@@ -12,8 +12,15 @@ export const MIN_SUPPORTED_SERVER_WIRE_VERSION = 3;
|
|
|
12
12
|
/** Definitive private WebSocket close code for an incompatible declared peer. */
|
|
13
13
|
export const WIRE_INCOMPATIBLE_CLOSE_CODE = 4002;
|
|
14
14
|
|
|
15
|
+
/** Shape of the machine-readable compatibility metadata object. */
|
|
16
|
+
export interface WireCompatibility {
|
|
17
|
+
readonly protocolVersion: number;
|
|
18
|
+
readonly minimumServerProtocolVersion: number;
|
|
19
|
+
readonly incompatibleCloseCode: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
/** Machine-readable compatibility metadata shipped in every TS release. */
|
|
16
|
-
export const WIRE_COMPATIBILITY = Object.freeze({
|
|
23
|
+
export const WIRE_COMPATIBILITY: WireCompatibility = Object.freeze({
|
|
17
24
|
protocolVersion: WIRE_PROTOCOL_VERSION,
|
|
18
25
|
minimumServerProtocolVersion: MIN_SUPPORTED_SERVER_WIRE_VERSION,
|
|
19
26
|
incompatibleCloseCode: WIRE_INCOMPATIBLE_CLOSE_CODE,
|