@tuidom/terminal-backend 0.2.1 → 0.3.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/nodeTerminalBackend.d.ts +11 -0
- package/dist/nodeTerminalBackend.js +57 -15
- package/package.json +2 -2
|
@@ -16,6 +16,9 @@ export declare class NodeTerminalBackend implements ITerminalBackend {
|
|
|
16
16
|
private resizeCallbacks;
|
|
17
17
|
private oscResponseCallbacks;
|
|
18
18
|
private deviceReportCallbacks;
|
|
19
|
+
/** DA1-запросов отправлено / ответов получено — чтобы проба ждала именно свой DA1. */
|
|
20
|
+
private da1Sent;
|
|
21
|
+
private da1Received;
|
|
19
22
|
private stdin;
|
|
20
23
|
private stdout;
|
|
21
24
|
private onDataHandler;
|
|
@@ -69,6 +72,14 @@ export declare class NodeTerminalBackend implements ITerminalBackend {
|
|
|
69
72
|
onResize(callback: (size: Size) => void): void;
|
|
70
73
|
onOscResponse(callback: (code: number, data: string) => void): void;
|
|
71
74
|
probeKeyboardProtocol(onResult: (supported: boolean) => void): void;
|
|
75
|
+
probeTerminalVersion(onResult: (nameAndVersion: string | undefined) => void): void;
|
|
76
|
+
/**
|
|
77
|
+
* Send `query` followed by DA1 and collect device reports until *our* DA1 reply
|
|
78
|
+
* arrives (or the timeout fires), then call `finish` exactly once. Replies come back
|
|
79
|
+
* in request order, so the n-th DA1 reply closes the n-th probe — two probes in
|
|
80
|
+
* flight don't settle each other early.
|
|
81
|
+
*/
|
|
82
|
+
private probe;
|
|
72
83
|
/** Fan a parsed input batch out to the registered callbacks. */
|
|
73
84
|
private dispatchInput;
|
|
74
85
|
/** If a partial sequence is buffered, flush it after a short grace period (the rest never came). */
|
|
@@ -45,7 +45,13 @@ const BRACKETED_PASTE_DISABLE = "\x1b[?2004l";
|
|
|
45
45
|
*/
|
|
46
46
|
const KITTY_FLAGS_QUERY = "\x1b[?u";
|
|
47
47
|
const DA1_QUERY = "\x1b[c";
|
|
48
|
-
const
|
|
48
|
+
const PROBE_TIMEOUT_MS = 200;
|
|
49
|
+
/**
|
|
50
|
+
* Terminal name/version probe: XTVERSION (`CSI > 0 q`) → `DCS > | <name(version)> ST`,
|
|
51
|
+
* then DA1 as the same "all replies are in" sentinel — terminals without XTVERSION stay
|
|
52
|
+
* silent on the first query but always answer DA1.
|
|
53
|
+
*/
|
|
54
|
+
const XTVERSION_QUERY = "\x1b[>0q";
|
|
49
55
|
/**
|
|
50
56
|
* How long to hold a partial escape sequence that arrived at the end of a stdin read,
|
|
51
57
|
* waiting for the rest to follow (it does, on a split keypress over SSH/tmux). If nothing
|
|
@@ -82,6 +88,9 @@ export class NodeTerminalBackend {
|
|
|
82
88
|
resizeCallbacks = [];
|
|
83
89
|
oscResponseCallbacks = [];
|
|
84
90
|
deviceReportCallbacks = [];
|
|
91
|
+
/** DA1-запросов отправлено / ответов получено — чтобы проба ждала именно свой DA1. */
|
|
92
|
+
da1Sent = 0;
|
|
93
|
+
da1Received = 0;
|
|
85
94
|
stdin;
|
|
86
95
|
stdout;
|
|
87
96
|
onDataHandler = null;
|
|
@@ -155,28 +164,53 @@ export class NodeTerminalBackend {
|
|
|
155
164
|
this.oscResponseCallbacks.push(callback);
|
|
156
165
|
}
|
|
157
166
|
probeKeyboardProtocol(onResult) {
|
|
158
|
-
let settled = false;
|
|
159
167
|
let supported = false;
|
|
168
|
+
// Direct (not passthrough): the reply must reflect tmux's own Kitty support, per-pane.
|
|
169
|
+
this.probe(KITTY_FLAGS_QUERY, (report) => {
|
|
170
|
+
if (report === "kitty-flags")
|
|
171
|
+
supported = true;
|
|
172
|
+
}, () => {
|
|
173
|
+
onResult(supported);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
probeTerminalVersion(onResult) {
|
|
177
|
+
let nameAndVersion;
|
|
178
|
+
// Direct, как и проба клавиатуры: под tmux отвечает сам tmux — это его панель.
|
|
179
|
+
this.probe(XTVERSION_QUERY, (report, params) => {
|
|
180
|
+
if (report === "xtversion")
|
|
181
|
+
nameAndVersion = params;
|
|
182
|
+
}, () => {
|
|
183
|
+
onResult(nameAndVersion);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Send `query` followed by DA1 and collect device reports until *our* DA1 reply
|
|
188
|
+
* arrives (or the timeout fires), then call `finish` exactly once. Replies come back
|
|
189
|
+
* in request order, so the n-th DA1 reply closes the n-th probe — two probes in
|
|
190
|
+
* flight don't settle each other early.
|
|
191
|
+
*/
|
|
192
|
+
probe(query, onReport, finish) {
|
|
193
|
+
const myDa1 = ++this.da1Sent;
|
|
194
|
+
let settled = false;
|
|
160
195
|
let timer = null;
|
|
161
|
-
const
|
|
196
|
+
const settle = () => {
|
|
162
197
|
if (settled)
|
|
163
198
|
return;
|
|
164
199
|
settled = true;
|
|
165
200
|
if (timer !== null)
|
|
166
201
|
clearTimeout(timer);
|
|
167
|
-
|
|
202
|
+
finish();
|
|
168
203
|
};
|
|
169
|
-
this.deviceReportCallbacks.push((report) => {
|
|
204
|
+
this.deviceReportCallbacks.push((report, params) => {
|
|
170
205
|
if (settled)
|
|
171
206
|
return;
|
|
172
|
-
if (report
|
|
173
|
-
|
|
174
|
-
else
|
|
175
|
-
|
|
207
|
+
if (report !== "da1")
|
|
208
|
+
onReport(report, params);
|
|
209
|
+
else if (this.da1Received >= myDa1)
|
|
210
|
+
settle(); // our DA1 reply = all replies are in
|
|
176
211
|
});
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
timer = setTimeout(finish, KEYBOARD_PROBE_TIMEOUT_MS);
|
|
212
|
+
this.writeDirect(query + DA1_QUERY);
|
|
213
|
+
timer = setTimeout(settle, PROBE_TIMEOUT_MS);
|
|
180
214
|
}
|
|
181
215
|
/** Fan a parsed input batch out to the registered callbacks. */
|
|
182
216
|
dispatchInput(result) {
|
|
@@ -197,6 +231,8 @@ export class NodeTerminalBackend {
|
|
|
197
231
|
cb(oscToken.code, oscToken.data);
|
|
198
232
|
}
|
|
199
233
|
for (const report of result.deviceReports) {
|
|
234
|
+
if (report.report === "da1")
|
|
235
|
+
this.da1Received++;
|
|
200
236
|
for (const cb of this.deviceReportCallbacks)
|
|
201
237
|
cb(report.report, report.params);
|
|
202
238
|
}
|
|
@@ -271,11 +307,15 @@ export class NodeTerminalBackend {
|
|
|
271
307
|
this.stdout.write("\x1b[?1049h");
|
|
272
308
|
// Show cursor (will be positioned by the focused element)
|
|
273
309
|
this.stdout.write("\x1b[?25h");
|
|
274
|
-
// Enable
|
|
275
|
-
this.writeDirect(KITTY_ENABLE);
|
|
276
|
-
// Also enable xterm modifyOtherKeys — tmux ignores the Kitty push but honors this,
|
|
310
|
+
// Enable xterm modifyOtherKeys — tmux ignores the Kitty push but honors this,
|
|
277
311
|
// delivering Ctrl+Tab / Ctrl+Shift+<key> as CSI-u. Direct, per-pane.
|
|
312
|
+
// Порядок важен: ДО kitty-push. iTerm2 трактует любой `CSI > 4 ; n m` как сброс
|
|
313
|
+
// стека kitty-флагов (VT100Terminal.m: `_keyReportingFlags = 0` + `removeAllObjects`),
|
|
314
|
+
// так что modifyOtherKeys после push откатывал его в legacy — и Cmd терялся.
|
|
278
315
|
this.writeDirect(MODIFY_OTHER_KEYS_ENABLE);
|
|
316
|
+
// Enable Kitty Keyboard Protocol — direct, so tmux tracks it per-pane (no passthrough).
|
|
317
|
+
// Последним из клавиатурных режимов — см. выше.
|
|
318
|
+
this.writeDirect(KITTY_ENABLE);
|
|
279
319
|
// Enable mouse tracking (all-motion mode for hover/enter/leave) — direct, per-pane
|
|
280
320
|
this.writeDirect(MOUSE_TRACKING_ALL_ENABLE);
|
|
281
321
|
// Enable bracketed paste so pastes arrive as one text block — direct, per-pane
|
|
@@ -326,6 +366,8 @@ export class NodeTerminalBackend {
|
|
|
326
366
|
});
|
|
327
367
|
}
|
|
328
368
|
teardown() {
|
|
369
|
+
// Клавиатурные режимы снимаются в порядке, обратном setup (LIFO): сначала pop
|
|
370
|
+
// kitty-флагов, который setup положил последним, затем сброс modifyOtherKeys.
|
|
329
371
|
// Disable Kitty Keyboard Protocol — direct (matches the per-pane enable in setup)
|
|
330
372
|
this.writeDirect(KITTY_DISABLE);
|
|
331
373
|
// Reset modifyOtherKeys — direct (matches the per-pane enable in setup)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tuidom/terminal-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Experimental terminal UI DOM — real terminal backend for Node (tty, ANSI diffing, Kitty protocol). API is unstable.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,6 +28,6 @@
|
|
|
28
28
|
"./*": "./dist/*.js"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@tuidom/core": "0.
|
|
31
|
+
"@tuidom/core": "0.3.0"
|
|
32
32
|
}
|
|
33
33
|
}
|