@cplieger/web-terminal-engine 6.4.0-dev.6 → 6.4.0-dev.9
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 -0
- package/src/keyboard.ts +13 -14
- package/src/status-stream.ts +31 -31
package/jsr.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cplieger/web-terminal-engine",
|
|
3
|
-
"version": "6.4.0-dev.
|
|
3
|
+
"version": "6.4.0-dev.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"typecheck": "tsc -p tsconfig.json",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"@stryker-mutator/vitest-runner": "10.0.0",
|
|
23
23
|
"@types/pngjs": "6.0.5",
|
|
24
24
|
"@typescript/native": "npm:typescript@7.0.2",
|
|
25
|
-
"@vitest/browser-playwright": "5.0.
|
|
26
|
-
"@vitest/coverage-v8": "5.0.
|
|
25
|
+
"@vitest/browser-playwright": "5.0.5",
|
|
26
|
+
"@vitest/coverage-v8": "5.0.5",
|
|
27
27
|
"esbuild": "0.28.2",
|
|
28
28
|
"eslint": "10.12.0",
|
|
29
29
|
"fast-check": "4.10.2",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"prettier": "3.9.10",
|
|
34
34
|
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
35
35
|
"typescript-eslint": "8.71.2",
|
|
36
|
-
"vitest": "5.0.
|
|
36
|
+
"vitest": "5.0.5"
|
|
37
37
|
},
|
|
38
38
|
"overrides": {
|
|
39
39
|
"qs": "^6.15.2"
|
package/src/index.ts
CHANGED
package/src/keyboard.ts
CHANGED
|
@@ -339,10 +339,10 @@ function anyModifier(ev: KeyboardEvent): boolean {
|
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
/**
|
|
342
|
-
* The kitty "unicode-key-code"
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
* other single-character
|
|
342
|
+
* The kitty "unicode-key-code", read as the UNSHIFTED US-layout codepoint of
|
|
343
|
+
* the physical key: letters, digits and KITTY_CODE_BASE's punctuation come from
|
|
344
|
+
* ev.code, so a composed Option character or a shifted symbol maps to its base
|
|
345
|
+
* key; any other single-character key falls back to the case-folded ev.key.
|
|
346
346
|
*/
|
|
347
347
|
function kittyKeyCodepoint(ev: KeyboardEvent): number {
|
|
348
348
|
const code = ev.code;
|
|
@@ -431,6 +431,12 @@ function encodeKittyDisambiguate(ev: KeyboardEvent): KeyboardResult {
|
|
|
431
431
|
if (ev.metaKey && !ev.ctrlKey && !ev.altKey) {
|
|
432
432
|
return { kind: "ignore" };
|
|
433
433
|
}
|
|
434
|
+
// A desktop AltGr character arrives with ctrlKey and altKey clear, so it stays
|
|
435
|
+
// text: on Windows, Chromium and Firefox swap Ctrl+Alt for AltGraph whenever
|
|
436
|
+
// the chord types a character (https://chromestatus.com/feature/5059438092222464,
|
|
437
|
+
// https://searchfox.org/mozilla-central/source/widget/windows/KeyboardLayout.cpp).
|
|
438
|
+
// AltGraph must not exempt a key here: Firefox sets it on every macOS Option
|
|
439
|
+
// chord (widget/cocoa/nsCocoaUtils.mm), and Ctrl+Option+a is 97;7u.
|
|
434
440
|
if (ev.ctrlKey || ev.altKey || ev.metaKey) {
|
|
435
441
|
const cp = kittyKeyCodepoint(ev);
|
|
436
442
|
if (cp > 0) {
|
|
@@ -480,15 +486,6 @@ export function mapKeyboardEvent(ev: KeyboardEvent, modes: KeyboardModes): Keybo
|
|
|
480
486
|
}
|
|
481
487
|
}
|
|
482
488
|
|
|
483
|
-
// Browser-reserved meta combos (Cmd+R, Cmd+T, Cmd+Q, Cmd+W on Mac;
|
|
484
|
-
// Ctrl+R, Ctrl+T, Ctrl+W with no Shift on others). Let the browser
|
|
485
|
-
// handle them. Heuristic: only take Ctrl+letter when Shift is NOT
|
|
486
|
-
// held AND the letter is one of the ones xterm consumes (a-z),
|
|
487
|
-
// skipping the few that browsers commonly reserve.
|
|
488
|
-
// (Ctrl+R, Ctrl+T and Ctrl+W still go to the PTY: readline binds them to
|
|
489
|
-
// reverse search, transpose and word rubout, and full-screen programs bind
|
|
490
|
-
// them too. On macOS, Cmd reaches the browser's own combos.)
|
|
491
|
-
|
|
492
489
|
// Shift+PageUp / Shift+PageDown — local scrollback navigation
|
|
493
490
|
// (matches xterm.js KeyboardResultType.PAGE_UP/PAGE_DOWN).
|
|
494
491
|
if (ev.key === "PageUp" && ev.shiftKey && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {
|
|
@@ -590,7 +587,9 @@ export function mapKeyboardEvent(ev: KeyboardEvent, modes: KeyboardModes): Keybo
|
|
|
590
587
|
return { kind: "ignore" }; // let `input` event handle plain space
|
|
591
588
|
}
|
|
592
589
|
|
|
593
|
-
// Single printable character with modifiers.
|
|
590
|
+
// Single printable character with modifiers. Neither branch takes Meta, so
|
|
591
|
+
// the browser keeps Cmd+R/T/W/C/V; Ctrl+R/T/W go to the PTY, where readline
|
|
592
|
+
// binds them.
|
|
594
593
|
if (ev.key.length === 1) {
|
|
595
594
|
const ch = ev.key;
|
|
596
595
|
|
package/src/status-stream.ts
CHANGED
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
import { nextBackoffDelay } from "./reconnect.js";
|
|
9
9
|
|
|
10
|
+
/** KnownStatus is a status the server derives itself, one per Go terminal.Status*
|
|
11
|
+
* constant. */
|
|
12
|
+
export type KnownStatus =
|
|
13
|
+
"working" | "idle" | "input" | "done" | "exited" | "crashed" | "failed" | "warning";
|
|
14
|
+
|
|
10
15
|
/** SessionInfo is one session's wire shape: the JSON object the session REST
|
|
11
16
|
* API (GET/POST /api/sessions) returns per session, kept in lockstep with the Go
|
|
12
17
|
* terminal.SessionInfo by hand.
|
|
@@ -16,21 +21,14 @@ import { nextBackoffDelay } from "./reconnect.js";
|
|
|
16
21
|
* a CLIENT-DERIVED automatic title a client asked the server to remember. */
|
|
17
22
|
export interface SessionInfo {
|
|
18
23
|
readonly id: string;
|
|
19
|
-
/** the session's current status.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
* restart never renders as a failure. Neither is a progress state: they
|
|
28
|
-
* outrank everything, and nothing clears them. A consumer must tolerate an
|
|
29
|
-
* UNKNOWN status string from a newer server: the stream is parsed, not
|
|
30
|
-
* validated, and an unrecognised value is forwarded rather than dropped
|
|
31
|
-
* (forward compatibility across the wire floor). */
|
|
32
|
-
readonly status:
|
|
33
|
-
"working" | "idle" | "input" | "done" | "exited" | "crashed" | "failed" | "warning";
|
|
24
|
+
/** the session's current status. The server derives a KnownStatus from the
|
|
25
|
+
* program's OSC 7501 report, or from OSC 9;4 progress while it has sent none
|
|
26
|
+
* since a hard reset (RIS); "exited" and "crashed" mark the process's end
|
|
27
|
+
* and nothing clears them. A classified OSC 9 notification latches whatever
|
|
28
|
+
* string the server's classifier returns, and a newer server may add values,
|
|
29
|
+
* so a consumer must handle any other string: the stream forwards it
|
|
30
|
+
* unchanged. The engine's docs/sessions.md gives every mapping. */
|
|
31
|
+
readonly status: KnownStatus | (string & {});
|
|
34
32
|
readonly title: string;
|
|
35
33
|
/** the session's short public name, safe to put in a URL: 1 to 64 characters
|
|
36
34
|
* of `[A-Za-z0-9_-]`, unique among live sessions, and NOT a capability
|
|
@@ -72,11 +70,11 @@ export interface SessionInfo {
|
|
|
72
70
|
* view: a list built from that hybrid still names the live set exactly, so the
|
|
73
71
|
* server accepts it and it becomes the arrangement every device sees. */
|
|
74
72
|
readonly order?: number;
|
|
75
|
-
/** true once the session has emitted a genuine activity signal — OSC
|
|
76
|
-
* progress or a classified OSC 9 notification.
|
|
77
|
-
* Sticky for the session's life
|
|
78
|
-
* only when this is set; a program that
|
|
79
|
-
* keeps its tab dot hidden. */
|
|
73
|
+
/** true once the session has emitted a genuine activity signal — an OSC 7501
|
|
74
|
+
* report, OSC 9;4 progress or a classified OSC 9 notification.
|
|
75
|
+
* Sticky for the session's life, a hard reset (RIS) included. Consumers
|
|
76
|
+
* reveal the per-tab activity dot only when this is set; a program that
|
|
77
|
+
* emits none of the three (a plain shell) keeps its tab dot hidden. */
|
|
80
78
|
readonly reportsActivity?: boolean;
|
|
81
79
|
/** the session's SECONDARY activity: a host-reported background activity that
|
|
82
80
|
* OUTLIVES the turn, carried beside `status` and never merged into it. The
|
|
@@ -84,18 +82,19 @@ export interface SessionInfo {
|
|
|
84
82
|
* (stopped and resumable, nobody is being asked) and `"input"` (a background
|
|
85
83
|
* task is blocked on the user); absent or an empty string means no secondary
|
|
86
84
|
* activity, and a consumer must tolerate an UNKNOWN value from a newer server
|
|
87
|
-
* by rendering nothing for it.
|
|
88
|
-
*
|
|
89
|
-
* Typed `string`, deliberately NOT a union like `status`: that union's own doc
|
|
90
|
-
* already admits it must tolerate values outside itself, which forces a cast at
|
|
91
|
-
* every forward-compatible site. Absent from a server that reports none. */
|
|
85
|
+
* by rendering nothing for it. Absent from a server that reports none. */
|
|
92
86
|
readonly activity?: string;
|
|
93
87
|
/** how many sources produced `activity`: >= 1 whenever `activity` is non-empty,
|
|
94
88
|
* 0 when it is empty or the host does not count them. */
|
|
95
89
|
readonly activityCount?: number;
|
|
96
|
-
/** numbers the occurrence of
|
|
97
|
-
*
|
|
98
|
-
*
|
|
90
|
+
/** numbers the occurrence of the session's status. A sweep raises it by one
|
|
91
|
+
* when it emits a changed status, a status a classified notification latched
|
|
92
|
+
* since the last sweep, or an input, done or failed status after an OSC 7501
|
|
93
|
+
* report since then created a record, changed one's state or kind, or
|
|
94
|
+
* cleared one, and so changed which record the status came from, or its
|
|
95
|
+
* state or kind, at that report. A report keeping a record's state and kind
|
|
96
|
+
* raises it only by changing the status. It never falls for one id; 0,
|
|
97
|
+
* absent on the wire, means nothing numbered yet. */
|
|
99
98
|
readonly statusSeq?: number;
|
|
100
99
|
/** the highest `statusSeq` any reader acknowledged through
|
|
101
100
|
* `PUT /api/sessions/{id}/seen`. Never falls for one id and never exceeds
|
|
@@ -110,9 +109,10 @@ export interface SessionInfo {
|
|
|
110
109
|
export interface SessionStatus extends SessionInfo {
|
|
111
110
|
/** true when the session is gone (closed or reaped); the consumer drops it. */
|
|
112
111
|
readonly removed?: boolean;
|
|
113
|
-
/** the
|
|
114
|
-
*
|
|
115
|
-
*
|
|
112
|
+
/** the session's percentage: the OSC 7501 report's `progress` when it
|
|
113
|
+
* carries one, else the OSC 9;4 percentage. -1 when absent or unknown (no
|
|
114
|
+
* sequence seen, or a state that carries none — clear/indeterminate), else
|
|
115
|
+
* 0-100. Paired with `status` to render a determinate bar. */
|
|
116
116
|
readonly progressValue?: number;
|
|
117
117
|
/** the OSC 9 notification message, delivered to EVERY subscriber whether or
|
|
118
118
|
* not the server has a status classifier installed. A notification is an
|