poly-weaver 0.7.1 → 0.8.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.
- package/dist/ansi.d.ts +7 -0
- package/dist/ansi.d.ts.map +1 -1
- package/dist/ansi.js +30 -0
- package/dist/ansi.js.map +1 -1
- package/dist/app-header.d.ts +16 -0
- package/dist/app-header.d.ts.map +1 -0
- package/dist/app-header.js +74 -0
- package/dist/app-header.js.map +1 -0
- package/dist/cli.js +81 -59
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/dump/collector.d.ts +2 -1
- package/dist/dump/collector.d.ts.map +1 -1
- package/dist/dump/collector.js +1 -0
- package/dist/dump/collector.js.map +1 -1
- package/dist/dump/hotkey.d.ts +9 -30
- package/dist/dump/hotkey.d.ts.map +1 -1
- package/dist/dump/hotkey.js +15 -158
- package/dist/dump/hotkey.js.map +1 -1
- package/dist/dump/service.d.ts +3 -2
- package/dist/dump/service.d.ts.map +1 -1
- package/dist/dump/service.js +4 -1
- package/dist/dump/service.js.map +1 -1
- package/dist/dump/types.d.ts +5 -0
- package/dist/dump/types.d.ts.map +1 -1
- package/dist/flow/built-in/default-factory.d.ts +21 -0
- package/dist/flow/built-in/default-factory.d.ts.map +1 -0
- package/dist/flow/built-in/default-factory.js +155 -0
- package/dist/flow/built-in/default-factory.js.map +1 -0
- package/dist/flow/built-in/default.d.ts +4 -4
- package/dist/flow/built-in/default.d.ts.map +1 -1
- package/dist/flow/built-in/default.js +3 -141
- package/dist/flow/built-in/default.js.map +1 -1
- package/dist/flow/built-in/default.ts +5 -169
- package/dist/flow/built-in/why-so-serious-factory.d.ts +16 -0
- package/dist/flow/built-in/why-so-serious-factory.d.ts.map +1 -0
- package/dist/flow/built-in/why-so-serious-factory.js +187 -0
- package/dist/flow/built-in/why-so-serious-factory.js.map +1 -0
- package/dist/flow/built-in/why-so-serious.d.ts +3 -2
- package/dist/flow/built-in/why-so-serious.d.ts.map +1 -1
- package/dist/flow/built-in/why-so-serious.js +3 -164
- package/dist/flow/built-in/why-so-serious.js.map +1 -1
- package/dist/flow/custom/AUTHORING.md +13 -0
- package/dist/flow/custom/load.d.ts +16 -1
- package/dist/flow/custom/load.d.ts.map +1 -1
- package/dist/flow/custom/load.js +69 -18
- package/dist/flow/custom/load.js.map +1 -1
- package/dist/flow/dsl.js +1 -1
- package/dist/flow/dsl.js.map +1 -1
- package/dist/flow/executor.d.ts.map +1 -1
- package/dist/flow/executor.js +44 -8
- package/dist/flow/executor.js.map +1 -1
- package/dist/flow/types.d.ts +20 -2
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/flow/types.js.map +1 -1
- package/dist/flow-editor/tui.d.ts.map +1 -1
- package/dist/flow-editor/tui.js +9 -18
- package/dist/flow-editor/tui.js.map +1 -1
- package/dist/orchestrator.d.ts +43 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +230 -92
- package/dist/orchestrator.js.map +1 -1
- package/dist/providers/claude/completion-plan-mode.d.ts.map +1 -1
- package/dist/providers/claude/completion-plan-mode.js +5 -2
- package/dist/providers/claude/completion-plan-mode.js.map +1 -1
- package/dist/providers/claude/tool-tracking.d.ts +1 -0
- package/dist/providers/claude/tool-tracking.d.ts.map +1 -1
- package/dist/providers/claude/tool-tracking.js +3 -0
- package/dist/providers/claude/tool-tracking.js.map +1 -1
- package/dist/pty/spawn.d.ts.map +1 -1
- package/dist/pty/spawn.js +6 -1
- package/dist/pty/spawn.js.map +1 -1
- package/dist/pty/types.d.ts +7 -2
- package/dist/pty/types.d.ts.map +1 -1
- package/dist/resume-tui.d.ts +52 -0
- package/dist/resume-tui.d.ts.map +1 -0
- package/dist/resume-tui.js +344 -0
- package/dist/resume-tui.js.map +1 -0
- package/dist/session/flow-snapshot.d.ts +14 -0
- package/dist/session/flow-snapshot.d.ts.map +1 -0
- package/dist/session/flow-snapshot.js +28 -0
- package/dist/session/flow-snapshot.js.map +1 -0
- package/dist/session/list.d.ts +16 -0
- package/dist/session/list.d.ts.map +1 -0
- package/dist/session/list.js +89 -0
- package/dist/session/list.js.map +1 -0
- package/dist/session/lock.d.ts +26 -0
- package/dist/session/lock.d.ts.map +1 -0
- package/dist/session/lock.js +95 -0
- package/dist/session/lock.js.map +1 -0
- package/dist/session/manifest.d.ts +80 -0
- package/dist/session/manifest.d.ts.map +1 -0
- package/dist/session/manifest.js +2 -0
- package/dist/session/manifest.js.map +1 -0
- package/dist/session/persistence.d.ts +19 -0
- package/dist/session/persistence.d.ts.map +1 -0
- package/dist/session/persistence.js +68 -0
- package/dist/session/persistence.js.map +1 -0
- package/dist/session/process-start-time.d.ts +17 -0
- package/dist/session/process-start-time.d.ts.map +1 -0
- package/dist/session/process-start-time.js +99 -0
- package/dist/session/process-start-time.js.map +1 -0
- package/dist/session/restore.d.ts +31 -0
- package/dist/session/restore.d.ts.map +1 -0
- package/dist/session/restore.js +111 -0
- package/dist/session/restore.js.map +1 -0
- package/dist/session/resume.d.ts +5 -0
- package/dist/session/resume.d.ts.map +1 -0
- package/dist/session/resume.js +109 -0
- package/dist/session/resume.js.map +1 -0
- package/dist/session/session-store.d.ts +43 -0
- package/dist/session/session-store.d.ts.map +1 -0
- package/dist/session/session-store.js +134 -0
- package/dist/session/session-store.js.map +1 -0
- package/dist/startup-tui.d.ts +10 -11
- package/dist/startup-tui.d.ts.map +1 -1
- package/dist/startup-tui.js +25 -108
- package/dist/startup-tui.js.map +1 -1
- package/dist/status-bar.js +1 -26
- package/dist/status-bar.js.map +1 -1
- package/dist/terminal/host.d.ts +22 -9
- package/dist/terminal/host.d.ts.map +1 -1
- package/dist/terminal/host.js +67 -78
- package/dist/terminal/host.js.map +1 -1
- package/dist/terminal/input-router.d.ts +24 -103
- package/dist/terminal/input-router.d.ts.map +1 -1
- package/dist/terminal/input-router.js +63 -364
- package/dist/terminal/input-router.js.map +1 -1
- package/dist/terminal/key-event-source.d.ts +52 -0
- package/dist/terminal/key-event-source.d.ts.map +1 -0
- package/dist/terminal/key-event-source.js +31 -0
- package/dist/terminal/key-event-source.js.map +1 -0
- package/dist/terminal/key-to-bytes.d.ts +11 -0
- package/dist/terminal/key-to-bytes.d.ts.map +1 -0
- package/dist/terminal/key-to-bytes.js +81 -0
- package/dist/terminal/key-to-bytes.js.map +1 -0
- package/dist/terminal/koffi-loader.d.ts +14 -0
- package/dist/terminal/koffi-loader.d.ts.map +1 -1
- package/dist/terminal/koffi-loader.js.map +1 -1
- package/dist/terminal/render.d.ts +1 -2
- package/dist/terminal/render.d.ts.map +1 -1
- package/dist/terminal/render.js +1 -30
- package/dist/terminal/render.js.map +1 -1
- package/dist/terminal/stdin-byte-source.d.ts +27 -0
- package/dist/terminal/stdin-byte-source.d.ts.map +1 -0
- package/dist/terminal/stdin-byte-source.js +92 -0
- package/dist/terminal/stdin-byte-source.js.map +1 -0
- package/dist/terminal/win32-console-mode.d.ts.map +1 -1
- package/dist/terminal/win32-console-mode.js.map +1 -1
- package/dist/terminal/win32-console-source.d.ts +26 -0
- package/dist/terminal/win32-console-source.d.ts.map +1 -0
- package/dist/terminal/win32-console-source.js +275 -0
- package/dist/terminal/win32-console-source.js.map +1 -0
- package/dist/terminal/win32-key-translator.d.ts +26 -0
- package/dist/terminal/win32-key-translator.d.ts.map +1 -0
- package/dist/terminal/win32-key-translator.js +87 -0
- package/dist/terminal/win32-key-translator.js.map +1 -0
- package/dist/terminal-input.d.ts +10 -0
- package/dist/terminal-input.d.ts.map +1 -1
- package/dist/terminal-input.js +132 -113
- package/dist/terminal-input.js.map +1 -1
- package/dist/user/host-curate-prompt.d.ts +2 -1
- package/dist/user/host-curate-prompt.d.ts.map +1 -1
- package/dist/user/host-curate-prompt.js +13 -11
- package/dist/user/host-curate-prompt.js.map +1 -1
- package/dist/user/host-prompt.d.ts +4 -1
- package/dist/user/host-prompt.d.ts.map +1 -1
- package/dist/user/host-prompt.js +24 -6
- package/dist/user/host-prompt.js.map +1 -1
- package/dist/user/prompt.d.ts +7 -9
- package/dist/user/prompt.d.ts.map +1 -1
- package/dist/user/prompt.js +14 -23
- package/dist/user/prompt.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
import type { TerminalKey } from "../terminal-input.js";
|
|
1
2
|
import type { PtyHandle } from "../pty/types.js";
|
|
2
3
|
/**
|
|
3
|
-
* Active parent UI mode (e.g. user-review prompt). When attached,
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Active parent UI mode (e.g. user-review prompt). When attached, event
|
|
5
|
+
* arrays are routed to it BEFORE host shortcuts / wheel scroll (but
|
|
6
|
+
* AFTER the global dump hotkey check). A host-backed prompt that wants
|
|
7
|
+
* to own wheel/mouse input simply consumes the events by returning
|
|
7
8
|
* `true` (the default).
|
|
8
9
|
*/
|
|
9
10
|
export interface ParentUiMode {
|
|
10
|
-
/**
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Receive a batch of `TerminalKey` events. Return `true` (or `void`)
|
|
13
|
+
* to consume the entire batch; return `false` to let the router fall
|
|
14
|
+
* through to its own shortcut/child routing for the same events.
|
|
15
|
+
*/
|
|
16
|
+
onEvents(events: TerminalKey[]): boolean | void;
|
|
12
17
|
}
|
|
13
18
|
/**
|
|
14
19
|
* Number of lines to scroll for a single mouse-wheel event. Small enough
|
|
@@ -18,7 +23,7 @@ export declare const WHEEL_LINES_PER_EVENT = 3;
|
|
|
18
23
|
export interface InputRouterOptions {
|
|
19
24
|
/** Trigger a dump capture when Ctrl+] is observed. */
|
|
20
25
|
onDump?: () => void;
|
|
21
|
-
/** Host interrupt: invoked when
|
|
26
|
+
/** Host interrupt: invoked when Ctrl+C is observed and no parent UI consumes it. */
|
|
22
27
|
onInterrupt?: () => void;
|
|
23
28
|
/** Scroll back by N lines (older content). */
|
|
24
29
|
onScrollBackLines?: (lines: number) => void;
|
|
@@ -32,115 +37,31 @@ export interface InputRouterOptions {
|
|
|
32
37
|
onScrollToBottom?: () => void;
|
|
33
38
|
}
|
|
34
39
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
40
|
+
* Event-based input router. Operates on `TerminalKey[]` produced by a
|
|
41
|
+
* `KeyEventSource`; the byte-level state machine is internal to
|
|
42
|
+
* `StdinByteSource`'s parser.
|
|
38
43
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* whole sequences for those paths.
|
|
44
|
+
* Routing order on each event batch:
|
|
45
|
+
* 1. Dump hotkey check (Ctrl+]) — strips matching events before they
|
|
46
|
+
* reach the parent UI or child.
|
|
43
47
|
* 2. Active parent UI mode (if attached) — fully consumes by default.
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* vertical wheel events and dispatches scroll callbacks. Non-wheel
|
|
49
|
-
* SGR mouse events flow through unchanged. A small first-class
|
|
50
|
-
* buffer reassembles SGR mouse sequences split across chunks so
|
|
51
|
-
* the routing works regardless of whether the dump detector is
|
|
52
|
-
* configured.
|
|
53
|
-
* 4. Host shortcut routing on remaining bytes:
|
|
54
|
-
* - Ctrl+C (0x03) → `onInterrupt()`
|
|
55
|
-
* - PageUp (CSI 5~) → `onScrollPages(-1)`
|
|
56
|
-
* - PageDown (CSI 6~) → `onScrollPages(+1)`
|
|
57
|
-
* - Home (CSI 1~/H) → `onScrollToTop()`
|
|
58
|
-
* - End (CSI 4~/F) → `onScrollToBottom()`
|
|
59
|
-
* 5. Attached child PTY when `gateOpen` is true — `handle.write(forward)`.
|
|
60
|
-
* 6. Otherwise the chunk is dropped.
|
|
48
|
+
* 3. Wheel events → scrollback callbacks (consumed; never reach child).
|
|
49
|
+
* 4. Host shortcuts: Ctrl+C → onInterrupt; page/home/end → scrollback.
|
|
50
|
+
* 5. Remaining events → child PTY when `gateOpen` is true, encoded via
|
|
51
|
+
* `encodeKeysToBytes`. Otherwise dropped.
|
|
61
52
|
*/
|
|
62
53
|
export declare class InputRouter {
|
|
63
54
|
private readonly options;
|
|
64
55
|
private parentUiMode;
|
|
65
56
|
private childHandle;
|
|
66
|
-
private dumpDetector;
|
|
67
|
-
/** Buffered head of a split SGR mouse sequence (`ESC [ < … `). */
|
|
68
|
-
private sgrPartial;
|
|
69
|
-
private sgrPartialTimer;
|
|
70
57
|
constructor(options?: InputRouterOptions);
|
|
71
58
|
attachChild(handle: PtyHandle): void;
|
|
72
59
|
detachChild(handle?: PtyHandle): void;
|
|
73
60
|
attachParentUi(mode: ParentUiMode): void;
|
|
74
61
|
detachParentUi(mode?: ParentUiMode): void;
|
|
75
|
-
/** Process a
|
|
76
|
-
|
|
62
|
+
/** Process a batch of `TerminalKey` events. */
|
|
63
|
+
handleEvents(events: TerminalKey[]): void;
|
|
77
64
|
private forwardToChild;
|
|
78
|
-
/**
|
|
79
|
-
* Strip SGR mouse wheel events (`CSI < 64;…M` and `CSI < 65;…M`) from
|
|
80
|
-
* the chunk, dispatching scrollback callbacks. Non-wheel SGR mouse
|
|
81
|
-
* events (clicks, motion, releases) and other CSI sequences (Home,
|
|
82
|
-
* PageUp, etc.) are left in the chunk so callers can still forward
|
|
83
|
-
* them or run them through host shortcut routing.
|
|
84
|
-
*
|
|
85
|
-
* Sequences split across chunk boundaries are first-class. The buffer
|
|
86
|
-
* holds any incomplete CSI prefix — bare ESC, ESC `[`, ESC `[ < …` —
|
|
87
|
-
* and prepends it to the next chunk. This works even when the dump
|
|
88
|
-
* detector is not configured (the detector reassembles split CSI for
|
|
89
|
-
* its own purposes when it is configured, but mouse-wheel scrollback
|
|
90
|
-
* MUST NOT depend on it). A 50 ms safety timer flushes a stale
|
|
91
|
-
* partial through `consumeHostShortcuts` and the child gate so a
|
|
92
|
-
* bare Esc keypress eventually reaches the child even when there is
|
|
93
|
-
* no continuation, and so a stray ESC byte cannot permanently block
|
|
94
|
-
* future input.
|
|
95
|
-
*/
|
|
96
|
-
private consumeSgrMouseWheel;
|
|
97
|
-
private savePartialSgr;
|
|
98
|
-
/**
|
|
99
|
-
* Stale-partial flush. Two distinct cases:
|
|
100
|
-
*
|
|
101
|
-
* - Bare `ESC` (1 byte): flush through host shortcuts and the child
|
|
102
|
-
* gate. The user pressed Esc and there is no continuation; the
|
|
103
|
-
* child must still receive the byte after the safety window
|
|
104
|
-
* (matching the dump detector's bare-Esc deferred-flush).
|
|
105
|
-
*
|
|
106
|
-
* - Truncated CSI (`ESC [ …`, 2+ bytes): drop. The user pressed
|
|
107
|
-
* Home/PageUp/wheel/etc. and the terminal failed to deliver the
|
|
108
|
-
* rest within the window. Forwarding raw `ESC [ < 6 4 ;` would
|
|
109
|
-
* inject random bytes into the child agent — much worse than
|
|
110
|
-
* losing one stray nav event.
|
|
111
|
-
*/
|
|
112
|
-
private flushPartialSgr;
|
|
113
|
-
private resetSgrPartialTimer;
|
|
114
|
-
private resetSgrPartial;
|
|
115
|
-
/**
|
|
116
|
-
* Consume host scrollback / interrupt shortcuts from the chunk. Returns
|
|
117
|
-
* the chunk with any consumed bytes removed.
|
|
118
|
-
*
|
|
119
|
-
* Recognized sequences (single-chunk only — split CSI across two
|
|
120
|
-
* chunks falls through to the child unchanged, which is acceptable
|
|
121
|
-
* because real terminals deliver these in one chunk):
|
|
122
|
-
*
|
|
123
|
-
* - 0x03 Ctrl+C → onInterrupt()
|
|
124
|
-
* - ESC [ 5 ~ PageUp → onScrollPages(-1)
|
|
125
|
-
* - ESC [ 6 ~ PageDown → onScrollPages(+1)
|
|
126
|
-
* - ESC [ 1 ~ / ESC [ H Home → onScrollToTop()
|
|
127
|
-
* - ESC [ 4 ~ / ESC [ F End → onScrollToBottom()
|
|
128
|
-
*
|
|
129
|
-
* Modifier-prefixed variants (e.g. `ESC [ 1;5 H` for Ctrl+Home) match
|
|
130
|
-
* the same callback as their unmodified form.
|
|
131
|
-
*/
|
|
132
|
-
private consumeHostShortcuts;
|
|
133
|
-
private dispatchCsi;
|
|
134
|
-
/**
|
|
135
|
-
* Route a deferred buffer (produced by `DumpHotkeyDetector` when a
|
|
136
|
-
* pending escape sequence times out). The deferred bytes have already
|
|
137
|
-
* been screened for the dump hotkey — they must NOT pass through the
|
|
138
|
-
* detector again. They DO continue through the parent UI mode, the
|
|
139
|
-
* SGR mouse parser, host shortcuts, and the child gate so a bare Esc
|
|
140
|
-
* reaches host-backed prompts and a deferred mouse sequence is still
|
|
141
|
-
* correctly classified.
|
|
142
|
-
*/
|
|
143
|
-
private routeDeferred;
|
|
144
65
|
destroy(): void;
|
|
145
66
|
}
|
|
146
67
|
//# sourceMappingURL=input-router.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input-router.d.ts","sourceRoot":"","sources":["../../src/terminal/input-router.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"input-router.d.ts","sourceRoot":"","sources":["../../src/terminal/input-router.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,GAAG,IAAI,CAAC;CACjD;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,8CAA8C;IAC9C,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,8EAA8E;IAC9E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,qCAAqC;IACrC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,sCAAsC;IACtC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,WAAW;IAIV,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,WAAW,CAAwB;gBAEd,OAAO,GAAE,kBAAuB;IAE7D,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAIpC,WAAW,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI;IAKrC,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAIxC,cAAc,CAAC,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI;IAKzC,+CAA+C;IAC/C,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI;IA4DzC,OAAO,CAAC,cAAc;IAatB,OAAO,IAAI,IAAI;CAIhB"}
|
|
@@ -1,67 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isDumpHotkey } from "../dump/hotkey.js";
|
|
2
|
+
import { encodeKeysToBytes } from "./key-to-bytes.js";
|
|
2
3
|
/**
|
|
3
4
|
* Number of lines to scroll for a single mouse-wheel event. Small enough
|
|
4
5
|
* to feel precise while still moving meaningfully on every notch.
|
|
5
6
|
*/
|
|
6
7
|
export const WHEEL_LINES_PER_EVENT = 3;
|
|
7
|
-
const ETX = 0x03; // Ctrl+C
|
|
8
|
-
const ESC = 0x1b;
|
|
9
|
-
const LBRACKET = 0x5b; // '['
|
|
10
|
-
const LT = 0x3c; // '<'
|
|
11
|
-
const TILDE = 0x7e; // '~'
|
|
12
|
-
/** Maximum dwell for a buffered partial SGR mouse sequence before we drop it. */
|
|
13
|
-
const SGR_MOUSE_PARTIAL_TIMEOUT_MS = 50;
|
|
14
8
|
/**
|
|
15
|
-
*
|
|
9
|
+
* Event-based input router. Operates on `TerminalKey[]` produced by a
|
|
10
|
+
* `KeyEventSource`; the byte-level state machine is internal to
|
|
11
|
+
* `StdinByteSource`'s parser.
|
|
16
12
|
*
|
|
17
|
-
* Routing order on each
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* The detector also reassembles split CSI sequences for kitty /
|
|
21
|
-
* win32 dump variants, so when configured, downstream stages see
|
|
22
|
-
* whole sequences for those paths.
|
|
13
|
+
* Routing order on each event batch:
|
|
14
|
+
* 1. Dump hotkey check (Ctrl+]) — strips matching events before they
|
|
15
|
+
* reach the parent UI or child.
|
|
23
16
|
* 2. Active parent UI mode (if attached) — fully consumes by default.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* vertical wheel events and dispatches scroll callbacks. Non-wheel
|
|
29
|
-
* SGR mouse events flow through unchanged. A small first-class
|
|
30
|
-
* buffer reassembles SGR mouse sequences split across chunks so
|
|
31
|
-
* the routing works regardless of whether the dump detector is
|
|
32
|
-
* configured.
|
|
33
|
-
* 4. Host shortcut routing on remaining bytes:
|
|
34
|
-
* - Ctrl+C (0x03) → `onInterrupt()`
|
|
35
|
-
* - PageUp (CSI 5~) → `onScrollPages(-1)`
|
|
36
|
-
* - PageDown (CSI 6~) → `onScrollPages(+1)`
|
|
37
|
-
* - Home (CSI 1~/H) → `onScrollToTop()`
|
|
38
|
-
* - End (CSI 4~/F) → `onScrollToBottom()`
|
|
39
|
-
* 5. Attached child PTY when `gateOpen` is true — `handle.write(forward)`.
|
|
40
|
-
* 6. Otherwise the chunk is dropped.
|
|
17
|
+
* 3. Wheel events → scrollback callbacks (consumed; never reach child).
|
|
18
|
+
* 4. Host shortcuts: Ctrl+C → onInterrupt; page/home/end → scrollback.
|
|
19
|
+
* 5. Remaining events → child PTY when `gateOpen` is true, encoded via
|
|
20
|
+
* `encodeKeysToBytes`. Otherwise dropped.
|
|
41
21
|
*/
|
|
42
22
|
export class InputRouter {
|
|
43
23
|
options;
|
|
44
24
|
parentUiMode;
|
|
45
25
|
childHandle;
|
|
46
|
-
dumpDetector;
|
|
47
|
-
/** Buffered head of a split SGR mouse sequence (`ESC [ < … `). */
|
|
48
|
-
sgrPartial = [];
|
|
49
|
-
sgrPartialTimer;
|
|
50
26
|
constructor(options = {}) {
|
|
51
27
|
this.options = options;
|
|
52
|
-
if (this.options.onDump) {
|
|
53
|
-
const onDump = this.options.onDump;
|
|
54
|
-
this.dumpDetector = new DumpHotkeyDetector(() => onDump(), (deferred) => {
|
|
55
|
-
// Deferred flush — fires when DumpHotkeyDetector buffered an
|
|
56
|
-
// ESC (or partial CSI) and the timeout expired without a final
|
|
57
|
-
// byte arriving. Re-enter the routing pipeline so a bare ESC
|
|
58
|
-
// reaches an active parent UI mode (HostBackedCuratePrompt
|
|
59
|
-
// uses Esc to cancel comment editing). Only when there is no
|
|
60
|
-
// parent UI and the child gate is open does the bytes reach
|
|
61
|
-
// the child.
|
|
62
|
-
this.routeDeferred(deferred);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
28
|
}
|
|
66
29
|
attachChild(handle) {
|
|
67
30
|
this.childHandle = handle;
|
|
@@ -79,346 +42,82 @@ export class InputRouter {
|
|
|
79
42
|
return;
|
|
80
43
|
this.parentUiMode = undefined;
|
|
81
44
|
}
|
|
82
|
-
/** Process a
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (forward.length === 0)
|
|
93
|
-
return true;
|
|
94
|
-
// Parent UI gets first crack at every byte that isn't the dump
|
|
95
|
-
// hotkey — including mouse events and nav keys. A host-backed
|
|
96
|
-
// prompt that wants to own wheel input simply consumes the chunk.
|
|
97
|
-
if (this.parentUiMode) {
|
|
98
|
-
const consumed = this.parentUiMode.onInput(forward);
|
|
99
|
-
// Default behaviour: parent UI swallows everything until detached.
|
|
100
|
-
if (consumed !== false) {
|
|
101
|
-
// The parent UI took ownership. Any partial SGR mouse sequence
|
|
102
|
-
// we previously buffered no longer applies — clear the buffer.
|
|
103
|
-
this.resetSgrPartial();
|
|
104
|
-
return true;
|
|
45
|
+
/** Process a batch of `TerminalKey` events. */
|
|
46
|
+
handleEvents(events) {
|
|
47
|
+
if (events.length === 0)
|
|
48
|
+
return;
|
|
49
|
+
// Pre-filter: dump hotkey runs ahead of everything and is stripped.
|
|
50
|
+
const filtered = [];
|
|
51
|
+
for (const ev of events) {
|
|
52
|
+
if (isDumpHotkey(ev)) {
|
|
53
|
+
this.options.onDump?.();
|
|
54
|
+
continue;
|
|
105
55
|
}
|
|
56
|
+
filtered.push(ev);
|
|
106
57
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return true;
|
|
115
|
-
return this.forwardToChild(afterShortcuts);
|
|
116
|
-
}
|
|
117
|
-
forwardToChild(buf) {
|
|
118
|
-
const child = this.childHandle;
|
|
119
|
-
if (!child || !child.gateOpen)
|
|
120
|
-
return false;
|
|
121
|
-
child.write(buf.toString());
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Strip SGR mouse wheel events (`CSI < 64;…M` and `CSI < 65;…M`) from
|
|
126
|
-
* the chunk, dispatching scrollback callbacks. Non-wheel SGR mouse
|
|
127
|
-
* events (clicks, motion, releases) and other CSI sequences (Home,
|
|
128
|
-
* PageUp, etc.) are left in the chunk so callers can still forward
|
|
129
|
-
* them or run them through host shortcut routing.
|
|
130
|
-
*
|
|
131
|
-
* Sequences split across chunk boundaries are first-class. The buffer
|
|
132
|
-
* holds any incomplete CSI prefix — bare ESC, ESC `[`, ESC `[ < …` —
|
|
133
|
-
* and prepends it to the next chunk. This works even when the dump
|
|
134
|
-
* detector is not configured (the detector reassembles split CSI for
|
|
135
|
-
* its own purposes when it is configured, but mouse-wheel scrollback
|
|
136
|
-
* MUST NOT depend on it). A 50 ms safety timer flushes a stale
|
|
137
|
-
* partial through `consumeHostShortcuts` and the child gate so a
|
|
138
|
-
* bare Esc keypress eventually reaches the child even when there is
|
|
139
|
-
* no continuation, and so a stray ESC byte cannot permanently block
|
|
140
|
-
* future input.
|
|
141
|
-
*/
|
|
142
|
-
consumeSgrMouseWheel(buf) {
|
|
143
|
-
// Prepend any buffered partial to this chunk before parsing.
|
|
144
|
-
if (this.sgrPartial.length > 0) {
|
|
145
|
-
const merged = Buffer.allocUnsafe(this.sgrPartial.length + buf.length);
|
|
146
|
-
for (let k = 0; k < this.sgrPartial.length; k++)
|
|
147
|
-
merged[k] = this.sgrPartial[k];
|
|
148
|
-
buf.copy(merged, this.sgrPartial.length);
|
|
149
|
-
buf = merged;
|
|
150
|
-
this.resetSgrPartialTimer();
|
|
151
|
-
this.sgrPartial = [];
|
|
58
|
+
if (filtered.length === 0)
|
|
59
|
+
return;
|
|
60
|
+
// Parent UI gets the remainder first.
|
|
61
|
+
if (this.parentUiMode) {
|
|
62
|
+
const consumed = this.parentUiMode.onEvents(filtered);
|
|
63
|
+
if (consumed !== false)
|
|
64
|
+
return;
|
|
152
65
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
out.push(b);
|
|
161
|
-
i++;
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
// ESC byte. The chunk ends here, after `[`, or after `[ < …`
|
|
165
|
-
// — buffer the partial in every case so a split mouse sequence
|
|
166
|
-
// never leaks to the child as half-bytes.
|
|
167
|
-
if (i + 1 >= buf.length) {
|
|
168
|
-
// Bare ESC at end-of-chunk. Could be (a) the Esc key, (b) the
|
|
169
|
-
// start of an Alt+key combo, or (c) the start of any CSI
|
|
170
|
-
// including SGR mouse. Buffering all three for 50 ms costs a
|
|
171
|
-
// small delay on the Esc key (matching the dump detector's
|
|
172
|
-
// own delay) but is the price of not forwarding partial mouse
|
|
173
|
-
// sequences to the child.
|
|
174
|
-
this.savePartialSgr(buf.subarray(i));
|
|
175
|
-
return Buffer.from(out);
|
|
176
|
-
}
|
|
177
|
-
if (buf[i + 1] !== LBRACKET) {
|
|
178
|
-
// ESC + non-`[` — Alt+key or another ESC-prefixed combo. Emit
|
|
179
|
-
// both bytes immediately so Alt+key combos are not delayed.
|
|
180
|
-
out.push(b);
|
|
181
|
-
i++;
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
// We have ESC `[`. Find the CSI final byte in the range 0x40-0x7e.
|
|
185
|
-
let j = i + 2;
|
|
186
|
-
while (j < buf.length) {
|
|
187
|
-
const t = buf[j];
|
|
188
|
-
if (t >= 0x40 && t <= 0x7e)
|
|
189
|
-
break;
|
|
190
|
-
j++;
|
|
191
|
-
}
|
|
192
|
-
if (j >= buf.length) {
|
|
193
|
-
// Unterminated CSI — buffer the partial for the next chunk to
|
|
194
|
-
// complete. This covers all reviewer-flagged splits: ESC alone,
|
|
195
|
-
// `ESC [`, `ESC [ <`, `ESC [ < 64 ;`, etc.
|
|
196
|
-
this.savePartialSgr(buf.subarray(i));
|
|
197
|
-
return Buffer.from(out);
|
|
198
|
-
}
|
|
199
|
-
const params = buf.subarray(i + 2, j).toString("ascii");
|
|
200
|
-
const final = buf[j];
|
|
201
|
-
// SGR mouse-wheel sequence: ESC `[ < cb ; cx ; cy M` where
|
|
202
|
-
// cb=64 (wheel up) or cb=65 (wheel down). Anything else (clicks,
|
|
203
|
-
// releases, non-mouse CSI) is passed through unchanged.
|
|
204
|
-
if (params.startsWith("<") && final === 0x4d /* M */) {
|
|
205
|
-
const cb = parseInt(params.slice(1).split(";")[0] ?? "", 10);
|
|
206
|
-
if (cb === 64) {
|
|
66
|
+
// Host owns the batch — pull wheel events into scrollback, ETX into
|
|
67
|
+
// interrupt, page/home/end into scroll callbacks; forward whatever
|
|
68
|
+
// remains to the child via the encoder.
|
|
69
|
+
const forward = [];
|
|
70
|
+
for (const ev of filtered) {
|
|
71
|
+
if (ev.kind === "wheel") {
|
|
72
|
+
if (ev.direction === "up") {
|
|
207
73
|
this.options.onScrollBackLines?.(WHEEL_LINES_PER_EVENT);
|
|
208
|
-
i = j + 1;
|
|
209
|
-
continue;
|
|
210
74
|
}
|
|
211
|
-
|
|
75
|
+
else {
|
|
212
76
|
this.options.onScrollForwardLines?.(WHEEL_LINES_PER_EVENT);
|
|
213
|
-
i = j + 1;
|
|
214
|
-
continue;
|
|
215
77
|
}
|
|
78
|
+
continue;
|
|
216
79
|
}
|
|
217
|
-
|
|
218
|
-
for (let k = i; k <= j; k++)
|
|
219
|
-
out.push(buf[k]);
|
|
220
|
-
i = j + 1;
|
|
221
|
-
}
|
|
222
|
-
return Buffer.from(out);
|
|
223
|
-
}
|
|
224
|
-
savePartialSgr(partial) {
|
|
225
|
-
this.sgrPartial = Array.from(partial);
|
|
226
|
-
if (this.sgrPartialTimer)
|
|
227
|
-
clearTimeout(this.sgrPartialTimer);
|
|
228
|
-
this.sgrPartialTimer = setTimeout(() => this.flushPartialSgr(), SGR_MOUSE_PARTIAL_TIMEOUT_MS);
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Stale-partial flush. Two distinct cases:
|
|
232
|
-
*
|
|
233
|
-
* - Bare `ESC` (1 byte): flush through host shortcuts and the child
|
|
234
|
-
* gate. The user pressed Esc and there is no continuation; the
|
|
235
|
-
* child must still receive the byte after the safety window
|
|
236
|
-
* (matching the dump detector's bare-Esc deferred-flush).
|
|
237
|
-
*
|
|
238
|
-
* - Truncated CSI (`ESC [ …`, 2+ bytes): drop. The user pressed
|
|
239
|
-
* Home/PageUp/wheel/etc. and the terminal failed to deliver the
|
|
240
|
-
* rest within the window. Forwarding raw `ESC [ < 6 4 ;` would
|
|
241
|
-
* inject random bytes into the child agent — much worse than
|
|
242
|
-
* losing one stray nav event.
|
|
243
|
-
*/
|
|
244
|
-
flushPartialSgr() {
|
|
245
|
-
if (this.sgrPartial.length === 0) {
|
|
246
|
-
this.sgrPartialTimer = undefined;
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const buf = Buffer.from(this.sgrPartial);
|
|
250
|
-
this.sgrPartial = [];
|
|
251
|
-
this.sgrPartialTimer = undefined;
|
|
252
|
-
if (buf.length === 1 && buf[0] === ESC) {
|
|
253
|
-
const afterShortcuts = this.consumeHostShortcuts(buf);
|
|
254
|
-
if (afterShortcuts.length > 0)
|
|
255
|
-
this.forwardToChild(afterShortcuts);
|
|
256
|
-
}
|
|
257
|
-
// Else: drop the truncated CSI prefix.
|
|
258
|
-
}
|
|
259
|
-
resetSgrPartialTimer() {
|
|
260
|
-
if (this.sgrPartialTimer) {
|
|
261
|
-
clearTimeout(this.sgrPartialTimer);
|
|
262
|
-
this.sgrPartialTimer = undefined;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
resetSgrPartial() {
|
|
266
|
-
this.resetSgrPartialTimer();
|
|
267
|
-
if (this.sgrPartial.length > 0) {
|
|
268
|
-
this.sgrPartial = [];
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Consume host scrollback / interrupt shortcuts from the chunk. Returns
|
|
273
|
-
* the chunk with any consumed bytes removed.
|
|
274
|
-
*
|
|
275
|
-
* Recognized sequences (single-chunk only — split CSI across two
|
|
276
|
-
* chunks falls through to the child unchanged, which is acceptable
|
|
277
|
-
* because real terminals deliver these in one chunk):
|
|
278
|
-
*
|
|
279
|
-
* - 0x03 Ctrl+C → onInterrupt()
|
|
280
|
-
* - ESC [ 5 ~ PageUp → onScrollPages(-1)
|
|
281
|
-
* - ESC [ 6 ~ PageDown → onScrollPages(+1)
|
|
282
|
-
* - ESC [ 1 ~ / ESC [ H Home → onScrollToTop()
|
|
283
|
-
* - ESC [ 4 ~ / ESC [ F End → onScrollToBottom()
|
|
284
|
-
*
|
|
285
|
-
* Modifier-prefixed variants (e.g. `ESC [ 1;5 H` for Ctrl+Home) match
|
|
286
|
-
* the same callback as their unmodified form.
|
|
287
|
-
*/
|
|
288
|
-
consumeHostShortcuts(buf) {
|
|
289
|
-
if (buf.length === 0)
|
|
290
|
-
return buf;
|
|
291
|
-
const out = [];
|
|
292
|
-
let i = 0;
|
|
293
|
-
while (i < buf.length) {
|
|
294
|
-
const b = buf[i];
|
|
295
|
-
if (b === ETX) {
|
|
80
|
+
if (ev.kind === "control" && ev.byte === 0x03) {
|
|
296
81
|
if (this.options.onInterrupt) {
|
|
297
82
|
this.options.onInterrupt();
|
|
298
|
-
i++;
|
|
299
83
|
continue;
|
|
300
84
|
}
|
|
301
|
-
// No interrupt handler — fall through to child.
|
|
302
|
-
out.push(b);
|
|
303
|
-
i++;
|
|
304
|
-
continue;
|
|
305
85
|
}
|
|
306
|
-
if (
|
|
307
|
-
// Find the CSI final byte.
|
|
308
|
-
let j = i + 2;
|
|
309
|
-
while (j < buf.length) {
|
|
310
|
-
const t = buf[j];
|
|
311
|
-
if (t >= 0x40 && t <= 0x7e)
|
|
312
|
-
break;
|
|
313
|
-
j++;
|
|
314
|
-
}
|
|
315
|
-
if (j >= buf.length) {
|
|
316
|
-
// Unterminated — emit remainder unchanged.
|
|
317
|
-
for (let k = i; k < buf.length; k++)
|
|
318
|
-
out.push(buf[k]);
|
|
319
|
-
return Buffer.from(out);
|
|
320
|
-
}
|
|
321
|
-
const params = buf.subarray(i + 2, j).toString("ascii");
|
|
322
|
-
const final = buf[j];
|
|
323
|
-
const handled = this.dispatchCsi(params, final);
|
|
324
|
-
if (handled) {
|
|
325
|
-
i = j + 1;
|
|
326
|
-
continue;
|
|
327
|
-
}
|
|
328
|
-
// Not a host shortcut — pass through unchanged.
|
|
329
|
-
for (let k = i; k <= j; k++)
|
|
330
|
-
out.push(buf[k]);
|
|
331
|
-
i = j + 1;
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
|
-
out.push(b);
|
|
335
|
-
i++;
|
|
336
|
-
}
|
|
337
|
-
return Buffer.from(out);
|
|
338
|
-
}
|
|
339
|
-
dispatchCsi(params, final) {
|
|
340
|
-
// PageUp = ESC [ 5 ~ ; PageDown = ESC [ 6 ~ ; Home = ESC [ 1 ~ or H ;
|
|
341
|
-
// End = ESC [ 4 ~ or F. Modifier params (e.g. "1;5") are accepted so
|
|
342
|
-
// Ctrl/Shift+nav routes through the same scroll callback.
|
|
343
|
-
if (final === TILDE) {
|
|
344
|
-
const code = parseInt(params.split(";")[0] ?? "", 10);
|
|
345
|
-
if (code === 5) {
|
|
346
|
-
if (this.options.onScrollPages) {
|
|
347
|
-
this.options.onScrollPages(-1);
|
|
348
|
-
return true;
|
|
349
|
-
}
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
if (code === 6) {
|
|
86
|
+
if (ev.kind === "page") {
|
|
353
87
|
if (this.options.onScrollPages) {
|
|
354
|
-
this.options.onScrollPages(1);
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
return false;
|
|
358
|
-
}
|
|
359
|
-
if (code === 1 || code === 7) {
|
|
360
|
-
if (this.options.onScrollToTop) {
|
|
361
|
-
this.options.onScrollToTop();
|
|
362
|
-
return true;
|
|
363
|
-
}
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
366
|
-
if (code === 4 || code === 8) {
|
|
367
|
-
if (this.options.onScrollToBottom) {
|
|
368
|
-
this.options.onScrollToBottom();
|
|
369
|
-
return true;
|
|
88
|
+
this.options.onScrollPages(ev.direction === "up" ? -1 : 1);
|
|
89
|
+
continue;
|
|
370
90
|
}
|
|
371
|
-
return false;
|
|
372
91
|
}
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
if (final === 0x48 /* H */) {
|
|
376
|
-
if (this.options.onScrollToTop) {
|
|
92
|
+
if (ev.kind === "home" && this.options.onScrollToTop) {
|
|
377
93
|
this.options.onScrollToTop();
|
|
378
|
-
|
|
94
|
+
continue;
|
|
379
95
|
}
|
|
380
|
-
|
|
381
|
-
}
|
|
382
|
-
if (final === 0x46 /* F */) {
|
|
383
|
-
if (this.options.onScrollToBottom) {
|
|
96
|
+
if (ev.kind === "end" && this.options.onScrollToBottom) {
|
|
384
97
|
this.options.onScrollToBottom();
|
|
385
|
-
|
|
98
|
+
continue;
|
|
386
99
|
}
|
|
387
|
-
|
|
100
|
+
forward.push(ev);
|
|
388
101
|
}
|
|
389
|
-
|
|
102
|
+
if (forward.length === 0)
|
|
103
|
+
return;
|
|
104
|
+
this.forwardToChild(forward);
|
|
390
105
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
* SGR mouse parser, host shortcuts, and the child gate so a bare Esc
|
|
397
|
-
* reaches host-backed prompts and a deferred mouse sequence is still
|
|
398
|
-
* correctly classified.
|
|
399
|
-
*/
|
|
400
|
-
routeDeferred(buf) {
|
|
106
|
+
forwardToChild(events) {
|
|
107
|
+
const child = this.childHandle;
|
|
108
|
+
if (!child || !child.gateOpen)
|
|
109
|
+
return;
|
|
110
|
+
const buf = encodeKeysToBytes(events);
|
|
401
111
|
if (buf.length === 0)
|
|
402
|
-
return
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
const afterMouse = this.consumeSgrMouseWheel(buf);
|
|
411
|
-
if (afterMouse.length === 0)
|
|
412
|
-
return true;
|
|
413
|
-
const afterShortcuts = this.consumeHostShortcuts(afterMouse);
|
|
414
|
-
if (afterShortcuts.length === 0)
|
|
415
|
-
return true;
|
|
416
|
-
return this.forwardToChild(afterShortcuts);
|
|
112
|
+
return;
|
|
113
|
+
// Forward the encoded Buffer verbatim — the PtyHandle adapter is
|
|
114
|
+
// responsible for handing it to node-pty without lossy decoding.
|
|
115
|
+
// Going through a latin1 string here would corrupt multibyte UTF-8
|
|
116
|
+
// printable chars (e.g. "中" → "ä¸") because node-pty re-encodes
|
|
117
|
+
// the inbound string as UTF-8.
|
|
118
|
+
child.write(buf);
|
|
417
119
|
}
|
|
418
120
|
destroy() {
|
|
419
|
-
this.dumpDetector?.destroy();
|
|
420
|
-
this.dumpDetector = undefined;
|
|
421
|
-
this.resetSgrPartial();
|
|
422
121
|
this.childHandle = undefined;
|
|
423
122
|
this.parentUiMode = undefined;
|
|
424
123
|
}
|