@cplieger/web-terminal-engine 5.3.0 → 6.0.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/src/modes.ts CHANGED
@@ -1,31 +1,4 @@
1
- // DEC private mode state — THE ACTIVE SESSION'S VIEW (P3). This singleton
2
- // mirrors the modes of whichever session the live socket serves: the
3
- // connection module is the single writer (it applies every inbound modes
4
- // frame via applySnapshot AND, in a tabbed shell, synchronously restores the
5
- // target session's cached snapshot inside setSession — power-on defaults for
6
- // a session never seen). Readers therefore never observe another session's
7
- // modes after setSession returns; the only staleness left is the inherent
8
- // one-frame lag against the session's own server state. Used by:
9
- // - keyboard.ts arrow-key encoder: emits SS3 (ESC O letter) when
10
- // applicationCursor is true, CSI (ESC [ letter) otherwise.
11
- // - paste handling: wraps in \e[200~..\e[201~ only when
12
- // bracketedPaste is on; otherwise sends raw text.
13
- // - mouse.ts: encodes mouse events only when mouseMode > 0.
14
- //
15
- // Defaults: bracketedPaste starts true because most modern shells
16
- // (bash 4.4+, zsh, fish) enable it immediately on startup via
17
- // CSI ?2004h. Starting false would cause the first paste before the
18
- // server's modes frame arrives to be sent un-bracketed, which shells
19
- // interpret as typed input (potentially executing pasted commands
20
- // character-by-character). applicationCursor starts false (normal
21
- // mode) because that's the VT100 power-on default.
22
-
23
- /**
24
- * One session's complete DEC-mode mirror, as a typed value (P3). The
25
- * connection module caches one per session and applies it via applySnapshot;
26
- * the named-field shape (vs setModes' nine positional parameters) makes
27
- * field-order drift impossible for production writers.
28
- */
1
+ /** One session's complete DEC-mode mirror, as a typed value. */
29
2
  export interface ModeSnapshot {
30
3
  bracketedPaste: boolean;
31
4
  applicationCursor: boolean;
@@ -39,11 +12,12 @@ export interface ModeSnapshot {
39
12
  }
40
13
 
41
14
  /**
42
- * The VT power-on mode state — what a session that has never announced modes
43
- * is in. Matches this module's initial values (see the bracketedPaste
44
- * default rationale above).
15
+ * The VT power-on mode state, frozen: copy it (`{ ...POWER_ON_MODES }`) before
16
+ * changing a field. `bracketedPaste` starts true because modern shells enable
17
+ * DEC 2004 at startup, and a paste delivered un-bracketed before the server's
18
+ * first modes frame would be executed as typed input.
45
19
  */
46
- export const POWER_ON_MODES: Readonly<ModeSnapshot> = {
20
+ export const POWER_ON_MODES: Readonly<ModeSnapshot> = Object.freeze({
47
21
  bracketedPaste: true,
48
22
  applicationCursor: false,
49
23
  mouseSGR: false,
@@ -53,144 +27,96 @@ export const POWER_ON_MODES: Readonly<ModeSnapshot> = {
53
27
  reverseVideo: false,
54
28
  mousePixels: false,
55
29
  keyboardFlags: 0,
56
- };
30
+ });
57
31
 
58
- let bracketedPaste = true;
59
- let applicationCursor = false;
60
- let applicationKeypad = false;
61
- let mouseMode = 0; // 0=off, 1000=normal, 1002=button-event, 1003=any-event
62
- let mouseSGR = false;
63
- let mousePixels = false; // DEC 1016: report pixel coords instead of cell coords
64
- let focusReporting = false;
65
- let reverseVideo = false;
66
- // Kitty keyboard progressive-enhancement flags (bit0 disambiguate, bit1
67
- // event-types, bit2 alternate-keys). 0 = protocol disabled (legacy encoding).
68
- let keyboardFlags = 0;
69
-
70
- /**
71
- * Update the cached mode state. Called by the consumer whenever the server
72
- * sends a `ModesMessage`. Pass-through arguments are optional so older
73
- * server builds that don't include all fields don't reset them to defaults.
74
- */
75
- export function setModes(
76
- bracketed: boolean,
77
- appCursor: boolean,
78
- mSGR?: boolean,
79
- focus?: boolean,
80
- mMode?: number,
81
- appKeypad?: boolean,
82
- revVideo?: boolean,
83
- mPixels?: boolean,
84
- kbdFlags?: number,
85
- ): void {
86
- bracketedPaste = bracketed;
87
- applicationCursor = appCursor;
88
- if (mSGR !== undefined) {
89
- mouseSGR = mSGR;
90
- }
91
- if (focus !== undefined) {
92
- focusReporting = focus;
93
- }
94
- if (mMode !== undefined) {
95
- mouseMode = mMode;
96
- }
97
- if (appKeypad !== undefined) {
98
- applicationKeypad = appKeypad;
99
- }
100
- if (revVideo !== undefined) {
101
- reverseVideo = revVideo;
102
- }
103
- if (mPixels !== undefined) {
104
- mousePixels = mPixels;
105
- }
106
- if (kbdFlags !== undefined) {
107
- keyboardFlags = kbdFlags;
108
- }
32
+ /** The DEC private-mode state of one terminal instance. */
33
+ export interface ModeState {
34
+ /** True when the server has DEC 2004 (bracketed paste) enabled. */
35
+ isBracketedPaste(): boolean;
36
+ /** True when the server has DECCKM (application cursor keys) enabled. */
37
+ isApplicationCursor(): boolean;
38
+ /** Active mouse tracking mode (xterm DECSET): 0 = off, 1000 = normal, 1002 = button-event, 1003 = any-event. */
39
+ getMouseMode(): number;
40
+ /** True when the server has DEC 1006 (SGR mouse encoding) enabled. */
41
+ isMouseSGR(): boolean;
42
+ /** True when the server has DEC 1016 (SGR-pixels mouse) enabled: reports carry pixel coordinates. */
43
+ isMousePixels(): boolean;
44
+ /** True when the server has DEC 1004 (focus event reporting) enabled. */
45
+ isFocusReporting(): boolean;
46
+ /** True when the server has DECKPAM (application keypad) enabled. */
47
+ isApplicationKeypad(): boolean;
48
+ /** True when the server has DEC 5 (reverse video / DECSCNM) enabled. */
49
+ isReverseVideo(): boolean;
50
+ /**
51
+ * Kitty keyboard progressive-enhancement flags in effect (bit0 disambiguate,
52
+ * bit1 report-event-types, bit2 report-alternate-keys); 0 means legacy encoding.
53
+ */
54
+ getKeyboardFlags(): number;
55
+ /** Replaces every mode bit with the snapshot's; no field is left at its previous value. */
56
+ applySnapshot(s: Readonly<ModeSnapshot>): void;
57
+ /** The current mode state as a snapshot value (a copy; safe to retain). */
58
+ snapshot(): ModeSnapshot;
59
+ /** Resets every field to `POWER_ON_MODES` and makes `applySnapshot` a no-op. */
60
+ dispose(): void;
109
61
  }
110
62
 
111
- /**
112
- * Apply a complete mode snapshot — every field, no optional-leaves-unchanged
113
- * semantics (that is setModes' positional back-compat contract). The
114
- * connection module uses this for both inbound modes frames and the
115
- * synchronous per-session restore in setSession.
116
- */
117
- export function applySnapshot(s: Readonly<ModeSnapshot>): void {
118
- bracketedPaste = s.bracketedPaste;
119
- applicationCursor = s.applicationCursor;
120
- mouseSGR = s.mouseSGR;
121
- focusReporting = s.focusReporting;
122
- mouseMode = s.mouseMode;
123
- applicationKeypad = s.applicationKeypad;
124
- reverseVideo = s.reverseVideo;
125
- mousePixels = s.mousePixels;
126
- keyboardFlags = s.keyboardFlags;
127
- }
63
+ /** Creates independent mode state initialized from a copy of `initial` (default `POWER_ON_MODES`). */
64
+ export function createModeState(initial: Readonly<ModeSnapshot> = POWER_ON_MODES): ModeState {
65
+ let bracketedPaste = initial.bracketedPaste;
66
+ let applicationCursor = initial.applicationCursor;
67
+ let mouseSGR = initial.mouseSGR;
68
+ let focusReporting = initial.focusReporting;
69
+ let mouseMode = initial.mouseMode;
70
+ let applicationKeypad = initial.applicationKeypad;
71
+ let reverseVideo = initial.reverseVideo;
72
+ let mousePixels = initial.mousePixels;
73
+ let keyboardFlags = initial.keyboardFlags;
74
+ let disposed = false;
75
+
76
+ function assign(s: Readonly<ModeSnapshot>): void {
77
+ bracketedPaste = s.bracketedPaste;
78
+ applicationCursor = s.applicationCursor;
79
+ mouseSGR = s.mouseSGR;
80
+ focusReporting = s.focusReporting;
81
+ mouseMode = s.mouseMode;
82
+ applicationKeypad = s.applicationKeypad;
83
+ reverseVideo = s.reverseVideo;
84
+ mousePixels = s.mousePixels;
85
+ keyboardFlags = s.keyboardFlags;
86
+ }
128
87
 
129
- /** The current mode state as a snapshot value (a copy; safe to retain). */
130
- export function snapshot(): ModeSnapshot {
131
88
  return {
132
- bracketedPaste,
133
- applicationCursor,
134
- mouseSGR,
135
- focusReporting,
136
- mouseMode,
137
- applicationKeypad,
138
- reverseVideo,
139
- mousePixels,
140
- keyboardFlags,
89
+ isBracketedPaste: () => bracketedPaste,
90
+ isApplicationCursor: () => applicationCursor,
91
+ getMouseMode: () => mouseMode,
92
+ isMouseSGR: () => mouseSGR,
93
+ isMousePixels: () => mousePixels,
94
+ isFocusReporting: () => focusReporting,
95
+ isApplicationKeypad: () => applicationKeypad,
96
+ isReverseVideo: () => reverseVideo,
97
+ getKeyboardFlags: () => keyboardFlags,
98
+ applySnapshot(s: Readonly<ModeSnapshot>): void {
99
+ if (disposed) {
100
+ return;
101
+ }
102
+ assign(s);
103
+ },
104
+ snapshot(): ModeSnapshot {
105
+ return {
106
+ bracketedPaste,
107
+ applicationCursor,
108
+ mouseSGR,
109
+ focusReporting,
110
+ mouseMode,
111
+ applicationKeypad,
112
+ reverseVideo,
113
+ mousePixels,
114
+ keyboardFlags,
115
+ };
116
+ },
117
+ dispose(): void {
118
+ disposed = true;
119
+ assign(POWER_ON_MODES);
120
+ },
141
121
  };
142
122
  }
143
-
144
- /** True when the server has DEC 2004 (bracketed paste) enabled. */
145
- export function isBracketedPaste(): boolean {
146
- return bracketedPaste;
147
- }
148
-
149
- /** True when the server has DECCKM (application cursor keys) enabled. */
150
- export function isApplicationCursor(): boolean {
151
- return applicationCursor;
152
- }
153
-
154
- /**
155
- * Active mouse tracking mode (xterm DECSET): 0 = off, 1000 = normal,
156
- * 1002 = button-event, 1003 = any-event.
157
- */
158
- export function getMouseMode(): number {
159
- return mouseMode;
160
- }
161
-
162
- /** True when the server has DEC 1006 (SGR mouse encoding) enabled. */
163
- export function isMouseSGR(): boolean {
164
- return mouseSGR;
165
- }
166
-
167
- /** True when the server has DEC 1016 (SGR-pixels mouse) enabled: mouse reports
168
- * carry pixel coordinates instead of cell coordinates. */
169
- export function isMousePixels(): boolean {
170
- return mousePixels;
171
- }
172
-
173
- /** True when the server has DEC 1004 (focus event reporting) enabled. */
174
- export function isFocusReporting(): boolean {
175
- return focusReporting;
176
- }
177
-
178
- /** True when the server has DECKPAM (application keypad) enabled. */
179
- export function isApplicationKeypad(): boolean {
180
- return applicationKeypad;
181
- }
182
-
183
- /** True when the server has DEC 5 (reverse video / DECSCNM) enabled. */
184
- export function isReverseVideo(): boolean {
185
- return reverseVideo;
186
- }
187
-
188
- /**
189
- * Kitty keyboard progressive-enhancement flags currently in effect (bit0
190
- * disambiguate, bit1 report-event-types, bit2 report-alternate-keys). 0 means
191
- * the protocol is disabled and keys use legacy encoding. Read by keyboard.ts to
192
- * choose between legacy and kitty CSI-u encoding.
193
- */
194
- export function getKeyboardFlags(): number {
195
- return keyboardFlags;
196
- }