poly-weaver 1.1.0 → 1.1.1
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/dist/crosscheck/coordinator.d.ts.map +1 -1
- package/dist/crosscheck/coordinator.js +1 -0
- package/dist/crosscheck/coordinator.js.map +1 -1
- package/dist/crosscheck/live-agent-session.d.ts +3 -0
- package/dist/crosscheck/live-agent-session.d.ts.map +1 -1
- package/dist/crosscheck/live-agent-session.js +12 -2
- package/dist/crosscheck/live-agent-session.js.map +1 -1
- package/dist/offscreen/controller.d.ts +1 -1
- package/dist/offscreen/controller.d.ts.map +1 -1
- package/dist/offscreen/controller.js.map +1 -1
- package/dist/offscreen/fork.d.ts +2 -1
- package/dist/offscreen/fork.d.ts.map +1 -1
- package/dist/offscreen/fork.js +11 -2
- package/dist/offscreen/fork.js.map +1 -1
- package/dist/providers/copilot/trust.js +1 -1
- package/dist/providers/copilot/trust.js.map +1 -1
- package/dist/terminal/color-negotiation.d.ts +37 -0
- package/dist/terminal/color-negotiation.d.ts.map +1 -0
- package/dist/terminal/color-negotiation.js +154 -0
- package/dist/terminal/color-negotiation.js.map +1 -0
- package/dist/terminal/color-protocol.d.ts +36 -0
- package/dist/terminal/color-protocol.d.ts.map +1 -0
- package/dist/terminal/color-protocol.js +88 -0
- package/dist/terminal/color-protocol.js.map +1 -0
- package/dist/terminal/host-input-mode.d.ts.map +1 -1
- package/dist/terminal/host-input-mode.js +6 -2
- package/dist/terminal/host-input-mode.js.map +1 -1
- package/dist/terminal/host.d.ts +13 -4
- package/dist/terminal/host.d.ts.map +1 -1
- package/dist/terminal/host.js +139 -44
- package/dist/terminal/host.js.map +1 -1
- package/dist/terminal/input-session.d.ts +23 -0
- package/dist/terminal/input-session.d.ts.map +1 -0
- package/dist/terminal/input-session.js +97 -0
- package/dist/terminal/input-session.js.map +1 -0
- package/dist/terminal/key-event-source.d.ts +5 -1
- package/dist/terminal/key-event-source.d.ts.map +1 -1
- package/dist/terminal/key-event-source.js +1 -0
- package/dist/terminal/key-event-source.js.map +1 -1
- package/dist/terminal/stdin-byte-source.d.ts +13 -8
- package/dist/terminal/stdin-byte-source.d.ts.map +1 -1
- package/dist/terminal/stdin-byte-source.js +84 -40
- package/dist/terminal/stdin-byte-source.js.map +1 -1
- package/dist/terminal/virtual-terminal.d.ts +2 -0
- package/dist/terminal/virtual-terminal.d.ts.map +1 -1
- package/dist/terminal/virtual-terminal.js +17 -0
- package/dist/terminal/virtual-terminal.js.map +1 -1
- package/dist/terminal-input.d.ts +19 -2
- package/dist/terminal-input.d.ts.map +1 -1
- package/dist/terminal-input.js +162 -6
- package/dist/terminal-input.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,75 +1,114 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TerminalInputSession } from "./input-session.js";
|
|
2
2
|
import { disableHostInputModes, enableHostInputModes, enableRightClickPasteMouseMode, } from "./host-input-mode.js";
|
|
3
3
|
/**
|
|
4
4
|
* Default `KeyEventSource`. Owns raw-mode on its injected stdin and
|
|
5
5
|
* optional VT input-mode negotiation on its injected stdout.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* longer part of any consumer's surface.
|
|
7
|
+
* A private start owns a fresh input session through stop. Hosted sources
|
|
8
|
+
* borrow only a consumer lease; their owner's parser survives handoffs.
|
|
10
9
|
*/
|
|
11
10
|
export class StdinByteSource {
|
|
12
11
|
stdin;
|
|
13
12
|
out;
|
|
13
|
+
configuredShiftEnterBytes;
|
|
14
14
|
options;
|
|
15
|
-
parser;
|
|
16
15
|
subscribers = new Set();
|
|
17
|
-
|
|
16
|
+
ownership;
|
|
17
|
+
activation;
|
|
18
18
|
disableRightClickPasteMouseMode;
|
|
19
19
|
prevRawMode = false;
|
|
20
|
-
|
|
20
|
+
modesEnabled = false;
|
|
21
|
+
rawAcquired = false;
|
|
21
22
|
constructor(stdin, out = process.stdout, configuredShiftEnterBytes, options = {}) {
|
|
22
23
|
this.stdin = stdin;
|
|
23
24
|
this.out = out;
|
|
25
|
+
this.configuredShiftEnterBytes = configuredShiftEnterBytes;
|
|
24
26
|
this.options = options;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
if (options.inputSession && options.inputSession.stdin !== stdin) {
|
|
28
|
+
throw new Error("Borrowed terminal input session uses a different stream");
|
|
29
|
+
}
|
|
30
|
+
options.inputSession?.assertLive();
|
|
31
|
+
this.ownership = options.inputSession
|
|
32
|
+
? { kind: "borrowed", session: options.inputSession }
|
|
33
|
+
: { kind: "private" };
|
|
27
34
|
}
|
|
28
35
|
start() {
|
|
29
|
-
if (this.
|
|
36
|
+
if (this.activation)
|
|
30
37
|
return;
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
this.
|
|
35
|
-
|
|
36
|
-
if (this.
|
|
37
|
-
this.
|
|
38
|
+
const session = this.ownership.kind === "borrowed" ? this.ownership.session
|
|
39
|
+
: new TerminalInputSession(this.stdin, this.configuredShiftEnterBytes, this.options.clock);
|
|
40
|
+
try {
|
|
41
|
+
const lease = session.acquire((events) => this.fanOut(events));
|
|
42
|
+
this.activation = { session, lease };
|
|
43
|
+
if (this.stdin.isTTY) {
|
|
44
|
+
this.prevRawMode = this.stdin.isRaw === true;
|
|
45
|
+
this.rawAcquired = true;
|
|
46
|
+
this.stdin.setRawMode(true);
|
|
47
|
+
enableHostInputModes(this.out);
|
|
48
|
+
this.modesEnabled = true;
|
|
49
|
+
if (this.options.enableRightClickPaste === true) {
|
|
50
|
+
this.disableRightClickPasteMouseMode = enableRightClickPasteMouseMode(this.out);
|
|
51
|
+
}
|
|
38
52
|
}
|
|
39
|
-
this.
|
|
53
|
+
lease.setEnabled(this.subscribers.size > 0);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
try {
|
|
57
|
+
this.stop();
|
|
58
|
+
}
|
|
59
|
+
catch (cleanupError) {
|
|
60
|
+
throw new AggregateError([error, cleanupError], "Terminal input start and cleanup failed");
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
if (this.ownership.kind === "private")
|
|
64
|
+
session.dispose();
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
40
67
|
}
|
|
41
|
-
this.dataListener = (chunk) => {
|
|
42
|
-
const events = this.parser.parseChunk(chunk);
|
|
43
|
-
if (events.length > 0)
|
|
44
|
-
this.fanOut(events);
|
|
45
|
-
};
|
|
46
|
-
this.stdin.on("data", this.dataListener);
|
|
47
68
|
}
|
|
48
69
|
stop() {
|
|
49
|
-
|
|
70
|
+
const activation = this.activation;
|
|
71
|
+
if (!activation)
|
|
50
72
|
return;
|
|
51
|
-
this.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.dataListener = undefined;
|
|
73
|
+
this.activation = undefined;
|
|
74
|
+
try {
|
|
75
|
+
activation.lease.release();
|
|
55
76
|
}
|
|
56
|
-
|
|
57
|
-
if (this.stdin.isTTY) {
|
|
58
|
-
this.disableRightClickPasteMouseMode?.();
|
|
59
|
-
this.disableRightClickPasteMouseMode = undefined;
|
|
60
|
-
disableHostInputModes(this.out);
|
|
77
|
+
finally {
|
|
61
78
|
try {
|
|
62
|
-
|
|
79
|
+
try {
|
|
80
|
+
this.disableRightClickPasteMouseMode?.();
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
this.disableRightClickPasteMouseMode = undefined;
|
|
84
|
+
if (this.modesEnabled) {
|
|
85
|
+
this.modesEnabled = false;
|
|
86
|
+
disableHostInputModes(this.out);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
try {
|
|
92
|
+
if (this.rawAcquired) {
|
|
93
|
+
this.rawAcquired = false;
|
|
94
|
+
this.stdin.setRawMode(this.prevRawMode);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
if (this.ownership.kind === "private")
|
|
99
|
+
activation.session.dispose();
|
|
100
|
+
}
|
|
63
101
|
}
|
|
64
|
-
catch { /* ignore */ }
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
this.disableRightClickPasteMouseMode = undefined;
|
|
68
102
|
}
|
|
69
103
|
}
|
|
70
104
|
onEvents(cb) {
|
|
71
105
|
this.subscribers.add(cb);
|
|
72
|
-
|
|
106
|
+
this.activation?.lease.setEnabled(true);
|
|
107
|
+
return () => {
|
|
108
|
+
this.subscribers.delete(cb);
|
|
109
|
+
if (this.subscribers.size === 0)
|
|
110
|
+
this.activation?.lease.setEnabled(false);
|
|
111
|
+
};
|
|
73
112
|
}
|
|
74
113
|
onResize(_cb) {
|
|
75
114
|
// libuv refreshes stdout.columns/stdout.rows and fires the stdout
|
|
@@ -78,11 +117,16 @@ export class StdinByteSource {
|
|
|
78
117
|
return () => { };
|
|
79
118
|
}
|
|
80
119
|
fanOut(events) {
|
|
120
|
+
const activation = this.activation;
|
|
121
|
+
if (!activation)
|
|
122
|
+
return;
|
|
81
123
|
// Snapshot only when multiple subscribers exist so a callback that
|
|
82
124
|
// unsubscribes itself does not perturb iteration. The typical single
|
|
83
125
|
// host subscriber goes through the direct path without an array copy.
|
|
84
126
|
if (this.subscribers.size > 1) {
|
|
85
127
|
for (const cb of Array.from(this.subscribers)) {
|
|
128
|
+
if (this.activation !== activation)
|
|
129
|
+
break;
|
|
86
130
|
try {
|
|
87
131
|
cb(events);
|
|
88
132
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdin-byte-source.js","sourceRoot":"","sources":["../../src/terminal/stdin-byte-source.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stdin-byte-source.js","sourceRoot":"","sources":["../../src/terminal/stdin-byte-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { IBuffer, IBufferCell, IMarker } from "@xterm/headless";
|
|
2
|
+
import { type ColorQuery } from "./color-protocol.js";
|
|
2
3
|
export interface VirtualTerminalOptions {
|
|
3
4
|
onOscClipboard?: (data: string) => void;
|
|
5
|
+
onColorQuery?: (query: ColorQuery) => void;
|
|
4
6
|
}
|
|
5
7
|
/**
|
|
6
8
|
* Wraps `@xterm/headless` so the rest of the host doesn't need to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"virtual-terminal.d.ts","sourceRoot":"","sources":["../../src/terminal/virtual-terminal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,OAAO,EAER,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"virtual-terminal.d.ts","sourceRoot":"","sources":["../../src/terminal/virtual-terminal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,OAAO,EAER,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAIzE,MAAM,WAAW,sBAAsB;IACrC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CAC5C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,kBAAkB,CAAQ;gBAEtB,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,sBAA2B;IAiDzE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI;IAQ7D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIxC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,0DAA0D;IAC1D,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,oFAAoF;IACpF,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,8CAA8C;IAC9C,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,8CAA8C;IAC9C,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,gDAAgD;IAChD,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,yEAAyE;IACzE,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,oFAAoF;IACpF,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED,wDAAwD;IACxD,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,0DAA0D;IAC1D,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,gFAAgF;IAChF,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,2DAA2D;IAC3D,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK/B,8DAA8D;IAC9D,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKlC,8EAA8E;IAC9E,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKpC,uDAAuD;IACvD,WAAW,IAAI,IAAI;IAInB,mDAAmD;IACnD,cAAc,IAAI,IAAI;IAItB,2DAA2D;IAC3D,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAKnD,oFAAoF;IACpF,OAAO,IAAI,WAAW;IAItB,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import xtermHeadless from "@xterm/headless";
|
|
2
2
|
import { parseOsc52 } from "./osc52.js";
|
|
3
|
+
import { parseColorQueries } from "./color-protocol.js";
|
|
3
4
|
const { Terminal } = xtermHeadless;
|
|
4
5
|
/**
|
|
5
6
|
* Wraps `@xterm/headless` so the rest of the host doesn't need to
|
|
@@ -41,6 +42,22 @@ export class VirtualTerminal {
|
|
|
41
42
|
}
|
|
42
43
|
return false;
|
|
43
44
|
});
|
|
45
|
+
for (const command of [4, 10, 11]) {
|
|
46
|
+
this.term.parser.registerOscHandler(command, (data) => {
|
|
47
|
+
for (const query of parseColorQueries(command, data))
|
|
48
|
+
opts.onColorQuery?.(query);
|
|
49
|
+
return true;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
for (const command of [104, 110, 111]) {
|
|
53
|
+
this.term.parser.registerOscHandler(command, () => true);
|
|
54
|
+
}
|
|
55
|
+
this.term.parser.registerCsiHandler({ prefix: "?", final: "n" }, (params) => {
|
|
56
|
+
if (params.length !== 1 || params[0] !== 996)
|
|
57
|
+
return false;
|
|
58
|
+
opts.onColorQuery?.({ kind: "appearance" });
|
|
59
|
+
return true;
|
|
60
|
+
});
|
|
44
61
|
const onOscClipboard = opts.onOscClipboard;
|
|
45
62
|
if (onOscClipboard) {
|
|
46
63
|
this.term.parser.registerOscHandler(52, (data) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"virtual-terminal.js","sourceRoot":"","sources":["../../src/terminal/virtual-terminal.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAO5C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"virtual-terminal.js","sourceRoot":"","sources":["../../src/terminal/virtual-terminal.ts"],"names":[],"mappings":"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"}
|
package/dist/terminal-input.d.ts
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
* Shared terminal input parser — bytes → low-level key identities.
|
|
3
3
|
*
|
|
4
4
|
* This module owns the byte-level state machine (normal/esc/csi/utf8),
|
|
5
|
-
* kitty keyboard protocol parsing, and
|
|
5
|
+
* kitty keyboard protocol parsing, escape timeout and bounded color-report
|
|
6
|
+
* separation. Covered reports use a distinct callback, never key events.
|
|
6
7
|
* It produces {@link TerminalKey} events with no domain semantics.
|
|
7
8
|
*
|
|
8
9
|
* Consumers wrap this via composition and map TerminalKey → their own domain
|
|
9
10
|
* event types. EmacsInputParser is the shared readline-style mapper for
|
|
10
11
|
* prompt and TUI text-editing surfaces.
|
|
11
12
|
*/
|
|
13
|
+
import { type ColorReport, type InputClock } from "./terminal/color-protocol.js";
|
|
12
14
|
export type TerminalKey = {
|
|
13
15
|
kind: "printable";
|
|
14
16
|
char: string;
|
|
@@ -59,6 +61,7 @@ export type TerminalKey = {
|
|
|
59
61
|
export declare class TerminalKeyParser {
|
|
60
62
|
private readonly escapeTimeoutMs;
|
|
61
63
|
private readonly configuredShiftEnterBytes?;
|
|
64
|
+
private readonly clock;
|
|
62
65
|
private state;
|
|
63
66
|
private csiParams;
|
|
64
67
|
private legacyMouseBytes;
|
|
@@ -70,11 +73,25 @@ export declare class TerminalKeyParser {
|
|
|
70
73
|
private pasteEndMatch;
|
|
71
74
|
private configuredShiftEnterMatch;
|
|
72
75
|
private configuredShiftEnterTimer;
|
|
73
|
-
|
|
76
|
+
private reportCandidate;
|
|
77
|
+
private reportFrame;
|
|
78
|
+
private reportDeadline;
|
|
79
|
+
private reportTimer;
|
|
80
|
+
private reportCallback;
|
|
81
|
+
private disposed;
|
|
82
|
+
constructor(escapeTimeoutMs?: number, configuredShiftEnterBytes?: Buffer | undefined, clock?: InputClock);
|
|
83
|
+
onReport(cb: (report: ColorReport) => void): void;
|
|
84
|
+
dispose(): void;
|
|
74
85
|
onDeferredKeys(cb: (keys: TerminalKey[]) => void): void;
|
|
75
86
|
cancelPendingEscape(): void;
|
|
76
87
|
parseChunk(chunk: Buffer): TerminalKey[];
|
|
77
88
|
private processByte;
|
|
89
|
+
private armReportTimer;
|
|
90
|
+
private clearReport;
|
|
91
|
+
private expireReport;
|
|
92
|
+
private flushKeyboardPrefix;
|
|
93
|
+
private consumeReportByte;
|
|
94
|
+
private processKeyboardByte;
|
|
78
95
|
private processByteWithoutConfiguredShiftEnter;
|
|
79
96
|
private consumeConfiguredShiftEnterByte;
|
|
80
97
|
private startConfiguredShiftEnterTimer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-input.d.ts","sourceRoot":"","sources":["../src/terminal-input.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"terminal-input.d.ts","sourceRoot":"","sources":["../src/terminal-input.ts"],"names":[],"mappings":"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"}
|
package/dist/terminal-input.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Shared terminal input parser — bytes → low-level key identities.
|
|
3
3
|
*
|
|
4
4
|
* This module owns the byte-level state machine (normal/esc/csi/utf8),
|
|
5
|
-
* kitty keyboard protocol parsing, and
|
|
5
|
+
* kitty keyboard protocol parsing, escape timeout and bounded color-report
|
|
6
|
+
* separation. Covered reports use a distinct callback, never key events.
|
|
6
7
|
* It produces {@link TerminalKey} events with no domain semantics.
|
|
7
8
|
*
|
|
8
9
|
* Consumers wrap this via composition and map TerminalKey → their own domain
|
|
@@ -10,6 +11,7 @@
|
|
|
10
11
|
* prompt and TUI text-editing surfaces.
|
|
11
12
|
*/
|
|
12
13
|
import { PASTE_END } from "./ansi.js";
|
|
14
|
+
import { COLOR_FRAME_TIMEOUT_MS, COLOR_PREFIX_TIMEOUT_MS, COLOR_REPORT_HEADERS, MAX_COLOR_FRAME_BYTES, inputClock, parseColorReports, } from "./terminal/color-protocol.js";
|
|
13
15
|
const SUPPRESSED = { tag: "suppressed" };
|
|
14
16
|
const UNRECOGNIZED = { tag: "unrecognized" };
|
|
15
17
|
const START_PASTE = { tag: "start-paste" };
|
|
@@ -21,6 +23,7 @@ function keys(...k) {
|
|
|
21
23
|
export class TerminalKeyParser {
|
|
22
24
|
escapeTimeoutMs;
|
|
23
25
|
configuredShiftEnterBytes;
|
|
26
|
+
clock;
|
|
24
27
|
state = "normal";
|
|
25
28
|
csiParams = "";
|
|
26
29
|
legacyMouseBytes = [];
|
|
@@ -32,28 +35,57 @@ export class TerminalKeyParser {
|
|
|
32
35
|
pasteEndMatch = 0;
|
|
33
36
|
configuredShiftEnterMatch = [];
|
|
34
37
|
configuredShiftEnterTimer;
|
|
35
|
-
|
|
38
|
+
reportCandidate = "";
|
|
39
|
+
reportFrame;
|
|
40
|
+
reportDeadline = 0;
|
|
41
|
+
reportTimer;
|
|
42
|
+
reportCallback;
|
|
43
|
+
disposed = false;
|
|
44
|
+
constructor(escapeTimeoutMs = 50, configuredShiftEnterBytes, clock = inputClock) {
|
|
36
45
|
this.escapeTimeoutMs = escapeTimeoutMs;
|
|
37
46
|
this.configuredShiftEnterBytes = configuredShiftEnterBytes;
|
|
47
|
+
this.clock = clock;
|
|
48
|
+
}
|
|
49
|
+
onReport(cb) {
|
|
50
|
+
this.reportCallback = cb;
|
|
51
|
+
}
|
|
52
|
+
dispose() {
|
|
53
|
+
this.disposed = true;
|
|
54
|
+
this.cancelPendingEscape();
|
|
55
|
+
this.clearReport();
|
|
56
|
+
this.deferredCallback = undefined;
|
|
57
|
+
this.reportCallback = undefined;
|
|
58
|
+
this.state = "normal";
|
|
59
|
+
this.csiParams = "";
|
|
60
|
+
this.utf8Buffer = [];
|
|
61
|
+
this.legacyMouseBytes = [];
|
|
62
|
+
this.pasteBuffer = null;
|
|
63
|
+
this.pasteEndMatch = 0;
|
|
38
64
|
}
|
|
39
65
|
onDeferredKeys(cb) {
|
|
40
66
|
this.deferredCallback = cb;
|
|
41
67
|
}
|
|
42
68
|
cancelPendingEscape() {
|
|
43
69
|
if (this.escapeTimer != null) {
|
|
44
|
-
clearTimeout(this.escapeTimer);
|
|
70
|
+
this.clock.clearTimeout(this.escapeTimer);
|
|
45
71
|
this.escapeTimer = undefined;
|
|
46
72
|
}
|
|
47
73
|
this.cancelPendingConfiguredShiftEnter();
|
|
48
74
|
}
|
|
49
75
|
parseChunk(chunk) {
|
|
50
76
|
const out = [];
|
|
77
|
+
if (this.disposed)
|
|
78
|
+
return out;
|
|
51
79
|
for (const byte of chunk) {
|
|
80
|
+
if (this.disposed)
|
|
81
|
+
break;
|
|
52
82
|
this.processByte(byte, out);
|
|
53
83
|
}
|
|
54
84
|
// Bare ESC at end of chunk: start escape timeout
|
|
55
85
|
if (this.state === "esc" && this.escapeTimer == null) {
|
|
56
|
-
this.escapeTimer = setTimeout(() => {
|
|
86
|
+
this.escapeTimer = this.clock.setTimeout(() => {
|
|
87
|
+
if (this.disposed)
|
|
88
|
+
return;
|
|
57
89
|
this.escapeTimer = undefined;
|
|
58
90
|
if (this.state === "esc") {
|
|
59
91
|
this.state = "normal";
|
|
@@ -64,6 +96,128 @@ export class TerminalKeyParser {
|
|
|
64
96
|
return out;
|
|
65
97
|
}
|
|
66
98
|
processByte(byte, out) {
|
|
99
|
+
this.expireReport(out);
|
|
100
|
+
if (this.reportFrame) {
|
|
101
|
+
this.consumeReportByte(byte);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (this.reportCandidate) {
|
|
105
|
+
const candidate = this.reportCandidate + String.fromCharCode(byte);
|
|
106
|
+
if (COLOR_REPORT_HEADERS.some((header) => header.startsWith(candidate))) {
|
|
107
|
+
this.reportCandidate = candidate;
|
|
108
|
+
if (COLOR_REPORT_HEADERS.some((header) => header === candidate)) {
|
|
109
|
+
// Resolve earlier keyboard bytes only once this candidate is a report.
|
|
110
|
+
this.flushKeyboardPrefix(out);
|
|
111
|
+
this.reportFrame = { header: candidate, body: "", size: candidate.length, invalid: false, st: false };
|
|
112
|
+
this.reportCandidate = "";
|
|
113
|
+
this.armReportTimer(COLOR_FRAME_TIMEOUT_MS);
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const pending = this.reportCandidate;
|
|
118
|
+
this.clearReport();
|
|
119
|
+
for (const ch of pending)
|
|
120
|
+
this.processKeyboardByte(ch.charCodeAt(0), out);
|
|
121
|
+
// The mismatching byte is new input, not part of the replayed prefix.
|
|
122
|
+
if (byte === 0x1b) {
|
|
123
|
+
if (this.state === "esc")
|
|
124
|
+
this.flushKeyboardPrefix(out);
|
|
125
|
+
this.processByte(byte, out);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.processKeyboardByte(byte, out);
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (this.pasteBuffer === null && this.state === "normal" && byte === 0x1b) {
|
|
133
|
+
this.reportCandidate = "\x1b";
|
|
134
|
+
this.armReportTimer(Math.min(this.escapeTimeoutMs, COLOR_PREFIX_TIMEOUT_MS));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.processKeyboardByte(byte, out);
|
|
138
|
+
}
|
|
139
|
+
armReportTimer(ms) {
|
|
140
|
+
if (this.reportTimer !== undefined)
|
|
141
|
+
this.clock.clearTimeout(this.reportTimer);
|
|
142
|
+
this.reportDeadline = this.clock.now() + ms;
|
|
143
|
+
const deadline = this.reportDeadline;
|
|
144
|
+
const expire = () => {
|
|
145
|
+
if (this.disposed || this.reportDeadline !== deadline)
|
|
146
|
+
return;
|
|
147
|
+
const out = [];
|
|
148
|
+
this.expireReport(out);
|
|
149
|
+
if (this.reportDeadline === deadline) {
|
|
150
|
+
// Timer scheduling may round down fractional monotonic time. Keep the
|
|
151
|
+
// original deadline and retry rather than leaving a candidate stranded.
|
|
152
|
+
this.reportTimer = this.clock.setTimeout(expire, Math.max(1, Math.ceil(deadline - this.clock.now())));
|
|
153
|
+
}
|
|
154
|
+
if (out.length > 0)
|
|
155
|
+
this.deferredCallback?.(out);
|
|
156
|
+
};
|
|
157
|
+
this.reportTimer = this.clock.setTimeout(expire, ms);
|
|
158
|
+
}
|
|
159
|
+
clearReport() {
|
|
160
|
+
if (this.reportTimer !== undefined)
|
|
161
|
+
this.clock.clearTimeout(this.reportTimer);
|
|
162
|
+
this.reportTimer = undefined;
|
|
163
|
+
this.reportDeadline = 0;
|
|
164
|
+
this.reportFrame = undefined;
|
|
165
|
+
this.reportCandidate = "";
|
|
166
|
+
}
|
|
167
|
+
expireReport(out) {
|
|
168
|
+
if ((!this.reportCandidate && !this.reportFrame) || this.clock.now() < this.reportDeadline)
|
|
169
|
+
return;
|
|
170
|
+
const pending = this.reportCandidate;
|
|
171
|
+
this.clearReport();
|
|
172
|
+
for (const ch of pending)
|
|
173
|
+
this.processKeyboardByte(ch.charCodeAt(0), out);
|
|
174
|
+
if (pending) {
|
|
175
|
+
// Do not give a bare Esc/configured prefix another recognition budget.
|
|
176
|
+
this.flushKeyboardPrefix(out);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
flushKeyboardPrefix(out) {
|
|
180
|
+
const configured = this.configuredShiftEnterMatch;
|
|
181
|
+
this.cancelPendingConfiguredShiftEnter();
|
|
182
|
+
for (const byte of configured)
|
|
183
|
+
this.processByteWithoutConfiguredShiftEnter(byte, out);
|
|
184
|
+
if (this.state === "esc") {
|
|
185
|
+
this.cancelPendingEscape();
|
|
186
|
+
this.state = "normal";
|
|
187
|
+
out.push({ kind: "escape" });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
consumeReportByte(byte) {
|
|
191
|
+
const frame = this.reportFrame;
|
|
192
|
+
if (byte === 0x18 || byte === 0x1a) {
|
|
193
|
+
this.clearReport();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const appearance = frame.header === "\x1b[?997;";
|
|
197
|
+
const final = appearance ? byte >= 0x40 && byte <= 0x7e
|
|
198
|
+
: byte === 0x07 || (frame.st && byte === 0x5c);
|
|
199
|
+
frame.size = Math.min(MAX_COLOR_FRAME_BYTES + 1, frame.size + 1);
|
|
200
|
+
if (final) {
|
|
201
|
+
const valid = !frame.invalid && frame.size <= MAX_COLOR_FRAME_BYTES
|
|
202
|
+
&& (appearance ? byte === 0x6e : !frame.st || byte === 0x5c);
|
|
203
|
+
const reports = valid ? parseColorReports(frame.header, frame.body) : [];
|
|
204
|
+
this.clearReport();
|
|
205
|
+
for (const report of reports) {
|
|
206
|
+
if (!this.disposed)
|
|
207
|
+
this.reportCallback?.(report);
|
|
208
|
+
}
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (frame.st || frame.size > MAX_COLOR_FRAME_BYTES
|
|
212
|
+
|| (byte !== 0x1b && (byte < 0x20 || byte > 0x7e))) {
|
|
213
|
+
frame.invalid = true;
|
|
214
|
+
frame.body = "";
|
|
215
|
+
}
|
|
216
|
+
frame.st = !appearance && byte === 0x1b;
|
|
217
|
+
if (!frame.invalid && !frame.st)
|
|
218
|
+
frame.body += String.fromCharCode(byte);
|
|
219
|
+
}
|
|
220
|
+
processKeyboardByte(byte, out) {
|
|
67
221
|
if (this.pasteBuffer === null
|
|
68
222
|
&& this.state === "normal"
|
|
69
223
|
&& this.consumeConfiguredShiftEnterByte(byte, out)) {
|
|
@@ -218,7 +372,9 @@ export class TerminalKeyParser {
|
|
|
218
372
|
startConfiguredShiftEnterTimer() {
|
|
219
373
|
if (this.configuredShiftEnterTimer !== undefined)
|
|
220
374
|
return;
|
|
221
|
-
this.configuredShiftEnterTimer = setTimeout(() => {
|
|
375
|
+
this.configuredShiftEnterTimer = this.clock.setTimeout(() => {
|
|
376
|
+
if (this.disposed)
|
|
377
|
+
return;
|
|
222
378
|
this.configuredShiftEnterTimer = undefined;
|
|
223
379
|
if (this.configuredShiftEnterMatch.length === 0)
|
|
224
380
|
return;
|
|
@@ -238,7 +394,7 @@ export class TerminalKeyParser {
|
|
|
238
394
|
}
|
|
239
395
|
cancelPendingConfiguredShiftEnter() {
|
|
240
396
|
if (this.configuredShiftEnterTimer !== undefined) {
|
|
241
|
-
clearTimeout(this.configuredShiftEnterTimer);
|
|
397
|
+
this.clock.clearTimeout(this.configuredShiftEnterTimer);
|
|
242
398
|
this.configuredShiftEnterTimer = undefined;
|
|
243
399
|
}
|
|
244
400
|
this.configuredShiftEnterMatch = [];
|