@oh-my-pi/pi-utils 18.6.1 → 18.6.3
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.6.3] - 2026-10-06
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- Replaced the `cursorPosition` option of `TerminalQueryResponder` with `hostCursorHandshake`, which leaves only the PTY host's own session-start cursor query unanswered instead of every cursor-position query
|
|
10
|
+
|
|
5
11
|
## [18.5.1] - 2026-10-03
|
|
6
12
|
|
|
7
13
|
### Added
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export interface TerminalQueryResponderOptions {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* The PTY host opens the session with its own cursor-position query
|
|
7
|
+
* (`CSI 6 n`), which the PTY layer has already answered — ConPTY's
|
|
8
|
+
* `PSEUDOCONSOLE_INHERIT_CURSOR` handshake. Leave that first cursor query
|
|
9
|
+
* unanswered: the host consumes exactly one report, so a second one would
|
|
10
|
+
* reach the program as input it never asked for. Every later cursor query
|
|
11
|
+
* is answered. Defaults to `false`.
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
+
hostCursorHandshake?: boolean;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Answers terminal capability queries emitted by programs on a headless PTY.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-utils",
|
|
3
|
-
"version": "18.6.
|
|
3
|
+
"version": "18.6.3",
|
|
4
4
|
"description": "Shared utilities for pi packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@oh-my-pi/pi-natives": "18.6.
|
|
60
|
+
"@oh-my-pi/pi-natives": "18.6.3"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@types/bun": "^1.3.14"
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export interface TerminalQueryResponderOptions {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* The PTY host opens the session with its own cursor-position query
|
|
7
|
+
* (`CSI 6 n`), which the PTY layer has already answered — ConPTY's
|
|
8
|
+
* `PSEUDOCONSOLE_INHERIT_CURSOR` handshake. Leave that first cursor query
|
|
9
|
+
* unanswered: the host consumes exactly one report, so a second one would
|
|
10
|
+
* reach the program as input it never asked for. Every later cursor query
|
|
11
|
+
* is answered. Defaults to `false`.
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
+
hostCursorHandshake?: boolean;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -31,10 +32,11 @@ export interface TerminalQueryResponderOptions {
|
|
|
31
32
|
export class TerminalQueryResponder {
|
|
32
33
|
/** Trailing bytes that may be the start of an unfinished query escape. */
|
|
33
34
|
#residual = "";
|
|
34
|
-
|
|
35
|
+
/** Whether the next cursor query is the host's already-answered session-start handshake. */
|
|
36
|
+
#hostCursorHandshakePending: boolean;
|
|
35
37
|
|
|
36
38
|
constructor(options: TerminalQueryResponderOptions = {}) {
|
|
37
|
-
this.#
|
|
39
|
+
this.#hostCursorHandshakePending = options.hostCursorHandshake ?? false;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
/**
|
|
@@ -48,7 +50,12 @@ export class TerminalQueryResponder {
|
|
|
48
50
|
QUERY.lastIndex = 0;
|
|
49
51
|
for (let match = QUERY.exec(buffer); match !== null; match = QUERY.exec(buffer)) {
|
|
50
52
|
lastEnd = match.index + match[0].length;
|
|
51
|
-
|
|
53
|
+
const reply = replyFor(match);
|
|
54
|
+
if (reply === CURSOR_POSITION_REPORT && this.#hostCursorHandshakePending) {
|
|
55
|
+
this.#hostCursorHandshakePending = false;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
replies += reply;
|
|
52
59
|
}
|
|
53
60
|
// Keep only a short unmatched trailing escape: a query split across
|
|
54
61
|
// chunks completes on the next feed, while a long tail is ordinary output
|
|
@@ -60,6 +67,9 @@ export class TerminalQueryResponder {
|
|
|
60
67
|
}
|
|
61
68
|
}
|
|
62
69
|
|
|
70
|
+
/** Cursor position report for the home position: there is no screen to locate the cursor on. */
|
|
71
|
+
const CURSOR_POSITION_REPORT = "\x1b[1;1R";
|
|
72
|
+
|
|
63
73
|
/** Longest query escape we answer, bounding the cross-chunk residual. */
|
|
64
74
|
const MAX_PARTIAL_QUERY = 32;
|
|
65
75
|
|
|
@@ -70,7 +80,7 @@ const MAX_PARTIAL_QUERY = 32;
|
|
|
70
80
|
const QUERY = /\x1b\[([?>=]?)([0-9;]*)([nc])|\x1b\](10|11);\?(\x07|\x1b\\)/gu;
|
|
71
81
|
|
|
72
82
|
/** Reply a real xterm-class terminal would send for one matched query. */
|
|
73
|
-
function replyFor(match: RegExpExecArray
|
|
83
|
+
function replyFor(match: RegExpExecArray): string {
|
|
74
84
|
const final = match[3];
|
|
75
85
|
if (final !== undefined) {
|
|
76
86
|
const intermediate = match[1];
|
|
@@ -82,7 +92,7 @@ function replyFor(match: RegExpExecArray, cursorPosition: boolean): string {
|
|
|
82
92
|
}
|
|
83
93
|
if (intermediate !== "") return ""; // private DSR forms (DECXCPR, appearance) stay unanswered
|
|
84
94
|
const selector = params.split(";", 1)[0];
|
|
85
|
-
if (selector === "6") return
|
|
95
|
+
if (selector === "6") return CURSOR_POSITION_REPORT;
|
|
86
96
|
if (selector === "5") return "\x1b[0n"; // device status: OK
|
|
87
97
|
return "";
|
|
88
98
|
}
|