@xterm/xterm 6.1.0-beta.22 → 6.1.0-beta.220

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.
Files changed (160) hide show
  1. package/README.md +62 -38
  2. package/css/xterm.css +29 -22
  3. package/lib/xterm.js +1 -1
  4. package/lib/xterm.js.map +1 -1
  5. package/lib/xterm.mjs +8 -34
  6. package/lib/xterm.mjs.map +4 -4
  7. package/package.json +25 -22
  8. package/src/browser/AccessibilityManager.ts +11 -6
  9. package/src/browser/Clipboard.ts +6 -3
  10. package/src/browser/CoreBrowserTerminal.ts +147 -318
  11. package/src/browser/Dom.ts +178 -0
  12. package/src/browser/Linkifier.ts +11 -11
  13. package/src/browser/OscLinkProvider.ts +3 -1
  14. package/src/browser/RenderDebouncer.ts +2 -2
  15. package/src/browser/TimeBasedDebouncer.ts +2 -2
  16. package/src/browser/Types.ts +12 -11
  17. package/src/browser/Viewport.ts +55 -20
  18. package/src/browser/decorations/BufferDecorationRenderer.ts +1 -1
  19. package/src/browser/decorations/OverviewRulerRenderer.ts +33 -17
  20. package/src/browser/input/CompositionHelper.ts +44 -8
  21. package/src/browser/public/Terminal.ts +25 -28
  22. package/src/browser/renderer/dom/DomRenderer.ts +242 -76
  23. package/src/browser/renderer/dom/DomRendererRowFactory.ts +19 -13
  24. package/src/browser/renderer/dom/WidthCache.ts +54 -52
  25. package/src/browser/renderer/shared/Constants.ts +7 -0
  26. package/src/browser/renderer/shared/TextBlinkStateManager.ts +97 -0
  27. package/src/browser/renderer/shared/Types.ts +8 -2
  28. package/src/browser/scrollable/abstractScrollbar.ts +300 -0
  29. package/src/browser/scrollable/fastDomNode.ts +126 -0
  30. package/src/browser/scrollable/globalPointerMoveMonitor.ts +90 -0
  31. package/src/browser/scrollable/horizontalScrollbar.ts +85 -0
  32. package/src/browser/scrollable/mouseEvent.ts +292 -0
  33. package/src/browser/scrollable/scrollable.ts +486 -0
  34. package/src/browser/scrollable/scrollableElement.ts +581 -0
  35. package/src/browser/scrollable/scrollableElementOptions.ts +161 -0
  36. package/src/browser/scrollable/scrollbarArrow.ts +110 -0
  37. package/src/browser/scrollable/scrollbarState.ts +246 -0
  38. package/src/browser/scrollable/scrollbarVisibilityController.ts +113 -0
  39. package/src/browser/scrollable/touch.ts +485 -0
  40. package/src/browser/scrollable/verticalScrollbar.ts +143 -0
  41. package/src/browser/scrollable/widget.ts +23 -0
  42. package/src/browser/services/CharSizeService.ts +2 -2
  43. package/src/browser/services/CoreBrowserService.ts +7 -5
  44. package/src/browser/services/KeyboardService.ts +67 -0
  45. package/src/browser/services/LinkProviderService.ts +1 -1
  46. package/src/browser/services/MouseCoordsService.ts +47 -0
  47. package/src/browser/services/MouseService.ts +518 -25
  48. package/src/browser/services/RenderService.ts +22 -15
  49. package/src/browser/services/SelectionService.ts +42 -35
  50. package/src/browser/services/Services.ts +40 -17
  51. package/src/browser/services/ThemeService.ts +2 -2
  52. package/src/common/Async.ts +105 -0
  53. package/src/common/CircularList.ts +2 -2
  54. package/src/common/Color.ts +8 -0
  55. package/src/common/CoreTerminal.ts +31 -21
  56. package/src/common/Event.ts +118 -0
  57. package/src/common/InputHandler.ts +283 -81
  58. package/src/common/Lifecycle.ts +113 -0
  59. package/src/common/Platform.ts +13 -3
  60. package/src/common/SortedList.ts +7 -3
  61. package/src/common/TaskQueue.ts +14 -5
  62. package/src/common/Types.ts +35 -15
  63. package/src/common/Version.ts +9 -0
  64. package/src/common/buffer/Buffer.ts +34 -19
  65. package/src/common/buffer/BufferLine.ts +133 -65
  66. package/src/common/buffer/BufferLineStringCache.ts +69 -0
  67. package/src/common/buffer/BufferSet.ts +11 -6
  68. package/src/common/buffer/CellData.ts +57 -0
  69. package/src/common/buffer/Marker.ts +2 -2
  70. package/src/common/buffer/Types.ts +6 -2
  71. package/src/common/data/EscapeSequences.ts +71 -70
  72. package/src/common/input/Keyboard.ts +14 -7
  73. package/src/common/input/KittyKeyboard.ts +526 -0
  74. package/src/common/input/Win32InputMode.ts +297 -0
  75. package/src/common/input/WriteBuffer.ts +64 -32
  76. package/src/common/input/XParseColor.ts +2 -2
  77. package/src/common/parser/ApcParser.ts +197 -0
  78. package/src/common/parser/Constants.ts +14 -4
  79. package/src/common/parser/DcsParser.ts +5 -5
  80. package/src/common/parser/EscapeSequenceParser.ts +205 -63
  81. package/src/common/parser/OscParser.ts +5 -5
  82. package/src/common/parser/Params.ts +27 -8
  83. package/src/common/parser/Types.ts +36 -2
  84. package/src/common/public/BufferLineApiView.ts +2 -2
  85. package/src/common/public/BufferNamespaceApi.ts +2 -2
  86. package/src/common/public/ParserApi.ts +3 -0
  87. package/src/common/services/BufferService.ts +14 -9
  88. package/src/common/services/CharsetService.ts +4 -0
  89. package/src/common/services/CoreService.ts +18 -4
  90. package/src/common/services/DecorationService.ts +24 -8
  91. package/src/common/services/LogService.ts +1 -31
  92. package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +21 -132
  93. package/src/common/services/OptionsService.ts +13 -4
  94. package/src/common/services/ServiceRegistry.ts +9 -7
  95. package/src/common/services/Services.ts +47 -40
  96. package/src/common/services/UnicodeService.ts +1 -1
  97. package/typings/xterm.d.ts +318 -34
  98. package/src/common/TypedArrayUtils.ts +0 -17
  99. package/src/vs/base/browser/browser.ts +0 -141
  100. package/src/vs/base/browser/canIUse.ts +0 -49
  101. package/src/vs/base/browser/dom.ts +0 -2369
  102. package/src/vs/base/browser/fastDomNode.ts +0 -316
  103. package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
  104. package/src/vs/base/browser/iframe.ts +0 -135
  105. package/src/vs/base/browser/keyboardEvent.ts +0 -213
  106. package/src/vs/base/browser/mouseEvent.ts +0 -229
  107. package/src/vs/base/browser/touch.ts +0 -372
  108. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
  109. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
  110. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
  111. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
  112. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
  113. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
  114. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
  115. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
  116. package/src/vs/base/browser/ui/widget.ts +0 -57
  117. package/src/vs/base/browser/window.ts +0 -14
  118. package/src/vs/base/common/arrays.ts +0 -887
  119. package/src/vs/base/common/arraysFind.ts +0 -202
  120. package/src/vs/base/common/assert.ts +0 -71
  121. package/src/vs/base/common/async.ts +0 -1992
  122. package/src/vs/base/common/cancellation.ts +0 -148
  123. package/src/vs/base/common/charCode.ts +0 -450
  124. package/src/vs/base/common/collections.ts +0 -140
  125. package/src/vs/base/common/decorators.ts +0 -130
  126. package/src/vs/base/common/equals.ts +0 -146
  127. package/src/vs/base/common/errors.ts +0 -303
  128. package/src/vs/base/common/event.ts +0 -1778
  129. package/src/vs/base/common/functional.ts +0 -32
  130. package/src/vs/base/common/hash.ts +0 -316
  131. package/src/vs/base/common/iterator.ts +0 -159
  132. package/src/vs/base/common/keyCodes.ts +0 -526
  133. package/src/vs/base/common/keybindings.ts +0 -284
  134. package/src/vs/base/common/lazy.ts +0 -47
  135. package/src/vs/base/common/lifecycle.ts +0 -801
  136. package/src/vs/base/common/linkedList.ts +0 -142
  137. package/src/vs/base/common/map.ts +0 -202
  138. package/src/vs/base/common/numbers.ts +0 -98
  139. package/src/vs/base/common/observable.ts +0 -76
  140. package/src/vs/base/common/observableInternal/api.ts +0 -31
  141. package/src/vs/base/common/observableInternal/autorun.ts +0 -281
  142. package/src/vs/base/common/observableInternal/base.ts +0 -489
  143. package/src/vs/base/common/observableInternal/debugName.ts +0 -145
  144. package/src/vs/base/common/observableInternal/derived.ts +0 -428
  145. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
  146. package/src/vs/base/common/observableInternal/logging.ts +0 -328
  147. package/src/vs/base/common/observableInternal/promise.ts +0 -209
  148. package/src/vs/base/common/observableInternal/utils.ts +0 -610
  149. package/src/vs/base/common/platform.ts +0 -281
  150. package/src/vs/base/common/scrollable.ts +0 -522
  151. package/src/vs/base/common/sequence.ts +0 -34
  152. package/src/vs/base/common/stopwatch.ts +0 -43
  153. package/src/vs/base/common/strings.ts +0 -557
  154. package/src/vs/base/common/symbols.ts +0 -9
  155. package/src/vs/base/common/uint.ts +0 -59
  156. package/src/vs/patches/nls.ts +0 -90
  157. package/src/vs/typings/base-common.d.ts +0 -20
  158. package/src/vs/typings/require.d.ts +0 -42
  159. package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
  160. package/src/vs/typings/vscode-globals-product.d.ts +0 -33
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Copyright (c) 2026 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ *
5
+ * Win32 input mode implementation.
6
+ * @see https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md
7
+ *
8
+ * Format: CSI Vk ; Sc ; Uc ; Kd ; Cs ; Rc _
9
+ * Vk: Virtual key code (decimal)
10
+ * Sc: Scan code (decimal)
11
+ * Uc: Unicode character (decimal codepoint, 0 if none)
12
+ * Kd: Key down (1) or up (0)
13
+ * Cs: Control key state (modifier flags)
14
+ * Rc: Repeat count (usually 1)
15
+ */
16
+
17
+ import { IKeyboardEvent, IKeyboardResult, KeyboardResultType } from 'common/Types';
18
+ import { C0 } from 'common/data/EscapeSequences';
19
+
20
+ /**
21
+ * Win32 control key state flags (from Windows API).
22
+ */
23
+ export const enum Win32ControlKeyState {
24
+ RIGHT_ALT_PRESSED = 0b000000001,
25
+ LEFT_ALT_PRESSED = 0b000000010,
26
+ RIGHT_CTRL_PRESSED = 0b000000100,
27
+ LEFT_CTRL_PRESSED = 0b000001000,
28
+ SHIFT_PRESSED = 0b000010000,
29
+ NUMLOCK_ON = 0b000100000,
30
+ SCROLLLOCK_ON = 0b001000000,
31
+ CAPSLOCK_ON = 0b010000000,
32
+ ENHANCED_KEY = 0b100000000,
33
+ }
34
+
35
+ /**
36
+ * Win32 input mode handler. Lookup tables are only initialized when this class
37
+ * is instantiated, reducing bundle size for environments that don't use this mode.
38
+ */
39
+ export class Win32InputMode {
40
+ /**
41
+ * Mapping from browser KeyboardEvent.code to Win32 virtual key codes.
42
+ * Based on https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
43
+ */
44
+ private readonly _codeToVk: { [code: string]: number } = {
45
+ // Letters
46
+ 'KeyA': 0x41, 'KeyB': 0x42, 'KeyC': 0x43, 'KeyD': 0x44, 'KeyE': 0x45,
47
+ 'KeyF': 0x46, 'KeyG': 0x47, 'KeyH': 0x48, 'KeyI': 0x49, 'KeyJ': 0x4A,
48
+ 'KeyK': 0x4B, 'KeyL': 0x4C, 'KeyM': 0x4D, 'KeyN': 0x4E, 'KeyO': 0x4F,
49
+ 'KeyP': 0x50, 'KeyQ': 0x51, 'KeyR': 0x52, 'KeyS': 0x53, 'KeyT': 0x54,
50
+ 'KeyU': 0x55, 'KeyV': 0x56, 'KeyW': 0x57, 'KeyX': 0x58, 'KeyY': 0x59,
51
+ 'KeyZ': 0x5A,
52
+
53
+ // Digits
54
+ 'Digit0': 0x30, 'Digit1': 0x31, 'Digit2': 0x32, 'Digit3': 0x33, 'Digit4': 0x34,
55
+ 'Digit5': 0x35, 'Digit6': 0x36, 'Digit7': 0x37, 'Digit8': 0x38, 'Digit9': 0x39,
56
+
57
+ // Function keys
58
+ 'F1': 0x70, 'F2': 0x71, 'F3': 0x72, 'F4': 0x73, 'F5': 0x74, 'F6': 0x75,
59
+ 'F7': 0x76, 'F8': 0x77, 'F9': 0x78, 'F10': 0x79, 'F11': 0x7A, 'F12': 0x7B,
60
+ 'F13': 0x7C, 'F14': 0x7D, 'F15': 0x7E, 'F16': 0x7F, 'F17': 0x80, 'F18': 0x81,
61
+ 'F19': 0x82, 'F20': 0x83, 'F21': 0x84, 'F22': 0x85, 'F23': 0x86, 'F24': 0x87,
62
+
63
+ // Numpad
64
+ 'Numpad0': 0x60, 'Numpad1': 0x61, 'Numpad2': 0x62, 'Numpad3': 0x63, 'Numpad4': 0x64,
65
+ 'Numpad5': 0x65, 'Numpad6': 0x66, 'Numpad7': 0x67, 'Numpad8': 0x68, 'Numpad9': 0x69,
66
+ 'NumpadMultiply': 0x6A, 'NumpadAdd': 0x6B, 'NumpadSeparator': 0x6C,
67
+ 'NumpadSubtract': 0x6D, 'NumpadDecimal': 0x6E, 'NumpadDivide': 0x6F,
68
+ 'NumpadEnter': 0x0D, // Same as Enter but with ENHANCED_KEY flag
69
+ 'NumLock': 0x90,
70
+
71
+ // Navigation
72
+ 'ArrowUp': 0x26, 'ArrowDown': 0x28, 'ArrowLeft': 0x25, 'ArrowRight': 0x27,
73
+ 'Home': 0x24, 'End': 0x23, 'PageUp': 0x21, 'PageDown': 0x22,
74
+ 'Insert': 0x2D, 'Delete': 0x2E,
75
+
76
+ // Modifiers
77
+ 'ShiftLeft': 0x10, 'ShiftRight': 0x10,
78
+ 'ControlLeft': 0x11, 'ControlRight': 0x11,
79
+ 'AltLeft': 0x12, 'AltRight': 0x12,
80
+ 'MetaLeft': 0x5B, 'MetaRight': 0x5C,
81
+ 'CapsLock': 0x14, 'ScrollLock': 0x91,
82
+
83
+ // Special keys
84
+ 'Escape': 0x1B, 'Enter': 0x0D, 'Tab': 0x09, 'Space': 0x20,
85
+ 'Backspace': 0x08, 'Pause': 0x13, 'ContextMenu': 0x5D, 'PrintScreen': 0x2C,
86
+
87
+ // OEM keys (US keyboard layout)
88
+ 'Semicolon': 0xBA, // ;:
89
+ 'Equal': 0xBB, // =+
90
+ 'Comma': 0xBC, // ,<
91
+ 'Minus': 0xBD, // -_
92
+ 'Period': 0xBE, // .>
93
+ 'Slash': 0xBF, // /?
94
+ 'Backquote': 0xC0, // `~
95
+ 'BracketLeft': 0xDB, // [{
96
+ 'Backslash': 0xDC, // \|
97
+ 'BracketRight': 0xDD, // ]}
98
+ 'Quote': 0xDE, // '"
99
+ 'IntlBackslash': 0xE2 // Non-US backslash
100
+ };
101
+
102
+ /**
103
+ * Mapping from browser KeyboardEvent.code to approximate Win32 scan codes.
104
+ * Note: Scan codes can vary by keyboard layout. These are approximations
105
+ * based on standard US keyboard layout.
106
+ */
107
+ private readonly _codeToScancode: { [code: string]: number } = {
108
+ // Letters (row by row)
109
+ 'KeyQ': 0x10, 'KeyW': 0x11, 'KeyE': 0x12, 'KeyR': 0x13, 'KeyT': 0x14,
110
+ 'KeyY': 0x15, 'KeyU': 0x16, 'KeyI': 0x17, 'KeyO': 0x18, 'KeyP': 0x19,
111
+ 'KeyA': 0x1E, 'KeyS': 0x1F, 'KeyD': 0x20, 'KeyF': 0x21, 'KeyG': 0x22,
112
+ 'KeyH': 0x23, 'KeyJ': 0x24, 'KeyK': 0x25, 'KeyL': 0x26,
113
+ 'KeyZ': 0x2C, 'KeyX': 0x2D, 'KeyC': 0x2E, 'KeyV': 0x2F, 'KeyB': 0x30,
114
+ 'KeyN': 0x31, 'KeyM': 0x32,
115
+
116
+ // Digits
117
+ 'Digit1': 0x02, 'Digit2': 0x03, 'Digit3': 0x04, 'Digit4': 0x05, 'Digit5': 0x06,
118
+ 'Digit6': 0x07, 'Digit7': 0x08, 'Digit8': 0x09, 'Digit9': 0x0A, 'Digit0': 0x0B,
119
+
120
+ // Function keys
121
+ 'F1': 0x3B, 'F2': 0x3C, 'F3': 0x3D, 'F4': 0x3E, 'F5': 0x3F, 'F6': 0x40,
122
+ 'F7': 0x41, 'F8': 0x42, 'F9': 0x43, 'F10': 0x44, 'F11': 0x57, 'F12': 0x58,
123
+
124
+ // Numpad
125
+ 'Numpad0': 0x52, 'Numpad1': 0x4F, 'Numpad2': 0x50, 'Numpad3': 0x51, 'Numpad4': 0x4B,
126
+ 'Numpad5': 0x4C, 'Numpad6': 0x4D, 'Numpad7': 0x47, 'Numpad8': 0x48, 'Numpad9': 0x49,
127
+ 'NumpadMultiply': 0x37, 'NumpadAdd': 0x4E, 'NumpadSubtract': 0x4A,
128
+ 'NumpadDecimal': 0x53, 'NumpadDivide': 0x35, 'NumpadEnter': 0x1C,
129
+ 'NumLock': 0x45,
130
+
131
+ // Navigation (extended keys)
132
+ 'ArrowUp': 0x48, 'ArrowDown': 0x50, 'ArrowLeft': 0x4B, 'ArrowRight': 0x4D,
133
+ 'Home': 0x47, 'End': 0x4F, 'PageUp': 0x49, 'PageDown': 0x51,
134
+ 'Insert': 0x52, 'Delete': 0x53,
135
+
136
+ // Modifiers
137
+ 'ShiftLeft': 0x2A, 'ShiftRight': 0x36,
138
+ 'ControlLeft': 0x1D, 'ControlRight': 0x1D,
139
+ 'AltLeft': 0x38, 'AltRight': 0x38,
140
+ 'CapsLock': 0x3A, 'ScrollLock': 0x46,
141
+
142
+ // Special keys
143
+ 'Escape': 0x01, 'Enter': 0x1C, 'Tab': 0x0F, 'Space': 0x39,
144
+ 'Backspace': 0x0E, 'Pause': 0x45,
145
+
146
+ // OEM keys
147
+ 'Semicolon': 0x27, 'Equal': 0x0D, 'Comma': 0x33, 'Minus': 0x0C,
148
+ 'Period': 0x34, 'Slash': 0x35, 'Backquote': 0x29,
149
+ 'BracketLeft': 0x1A, 'Backslash': 0x2B, 'BracketRight': 0x1B, 'Quote': 0x28
150
+ };
151
+
152
+ /**
153
+ * Codes that represent enhanced keys (extended keyboard keys).
154
+ */
155
+ private readonly _enhancedKeyCodes = new Set([
156
+ 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight',
157
+ 'Home', 'End', 'PageUp', 'PageDown', 'Insert', 'Delete',
158
+ 'NumpadEnter', 'NumpadDivide',
159
+ 'ControlRight', 'AltRight',
160
+ 'PrintScreen', 'Pause', 'ContextMenu',
161
+ 'MetaLeft', 'MetaRight'
162
+ ]);
163
+
164
+ /**
165
+ * Mapping of special keys (ev.key values) to their Unicode control character codes.
166
+ * These keys have multi-character ev.key strings but produce control characters.
167
+ * @see https://docs.microsoft.com/en-us/windows/console/key-event-record-str
168
+ */
169
+ private readonly _keyToControlChar: { [key: string]: number } = {
170
+ 'Enter': 0x0D, // Carriage return
171
+ 'Backspace': 0x08, // Backspace
172
+ 'Tab': 0x09, // Horizontal tab
173
+ 'Escape': 0x1B // Escape
174
+ };
175
+
176
+ /**
177
+ * Get the Win32 virtual key code for a keyboard event.
178
+ */
179
+ private _getVirtualKeyCode(ev: IKeyboardEvent): number {
180
+ const vk = this._codeToVk[ev.code];
181
+ if (vk !== undefined) {
182
+ return vk;
183
+ }
184
+ // Fall back to keyCode for unmapped keys
185
+ return ev.keyCode || 0;
186
+ }
187
+
188
+ /**
189
+ * Get the Win32 scan code for a keyboard event.
190
+ * Returns 0 if unknown (scan codes vary by hardware).
191
+ */
192
+ private _getScanCode(ev: IKeyboardEvent): number {
193
+ return this._codeToScancode[ev.code] || 0;
194
+ }
195
+
196
+ /**
197
+ * Get the unicode character for a keyboard event.
198
+ * Returns 0 for non-character keys.
199
+ */
200
+ private _getUnicodeChar(ev: IKeyboardEvent): number {
201
+ // Handle special keys that produce control characters
202
+ // Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL
203
+ if (ev.ctrlKey && !ev.altKey && !ev.metaKey) {
204
+ if (ev.key === 'Enter') {
205
+ return 0x0A; // Line feed (Ctrl+Enter)
206
+ }
207
+ if (ev.key === 'Backspace') {
208
+ return 0x7F; // DEL (Ctrl+Backspace)
209
+ }
210
+ }
211
+
212
+ // Check for special keys that always produce control characters
213
+ const controlChar = this._keyToControlChar[ev.key];
214
+ if (controlChar !== undefined) {
215
+ return controlChar;
216
+ }
217
+
218
+ // Only single-character keys produce unicode output
219
+ if (ev.key.length === 1) {
220
+ const codePoint = ev.key.codePointAt(0) || 0;
221
+
222
+ // Handle Ctrl+letter combinations - these produce control characters (0x01-0x1A)
223
+ if (ev.ctrlKey && !ev.altKey && !ev.metaKey) {
224
+ // Convert A-Z or a-z to control character (Ctrl+A = 0x01, Ctrl+C = 0x03, etc.)
225
+ if (codePoint >= 0x41 && codePoint <= 0x5A) { // A-Z
226
+ return codePoint - 0x40;
227
+ }
228
+ if (codePoint >= 0x61 && codePoint <= 0x7A) { // a-z
229
+ return codePoint - 0x60;
230
+ }
231
+ }
232
+
233
+ return codePoint;
234
+ }
235
+ return 0;
236
+ }
237
+
238
+ /**
239
+ * Get the Win32 control key state flags.
240
+ */
241
+ private _getControlKeyState(ev: IKeyboardEvent): number {
242
+ let state = 0;
243
+
244
+ if (ev.shiftKey) {
245
+ state |= Win32ControlKeyState.SHIFT_PRESSED;
246
+ }
247
+
248
+ // Note: We can't distinguish left/right for ctrl/alt in standard browser events,
249
+ // so we use the generic pressed flags. The right-side flags are used when
250
+ // we can detect them (e.g., via code property).
251
+ if (ev.ctrlKey) {
252
+ if (ev.code === 'ControlRight') {
253
+ state |= Win32ControlKeyState.RIGHT_CTRL_PRESSED;
254
+ } else {
255
+ state |= Win32ControlKeyState.LEFT_CTRL_PRESSED;
256
+ }
257
+ }
258
+
259
+ if (ev.altKey) {
260
+ if (ev.code === 'AltRight') {
261
+ state |= Win32ControlKeyState.RIGHT_ALT_PRESSED;
262
+ } else {
263
+ state |= Win32ControlKeyState.LEFT_ALT_PRESSED;
264
+ }
265
+ }
266
+
267
+ // Check for enhanced key
268
+ if (this._enhancedKeyCodes.has(ev.code)) {
269
+ state |= Win32ControlKeyState.ENHANCED_KEY;
270
+ }
271
+
272
+ return state;
273
+ }
274
+
275
+ /**
276
+ * Evaluate a keyboard event using Win32 input mode.
277
+ *
278
+ * @param ev The keyboard event.
279
+ * @param isKeyDown Whether this is a keydown (true) or keyup (false) event.
280
+ * @returns The keyboard result with the encoded key sequence.
281
+ */
282
+ public evaluateKeyboardEvent(ev: IKeyboardEvent, isKeyDown: boolean): IKeyboardResult {
283
+ const vk = this._getVirtualKeyCode(ev);
284
+ const sc = this._getScanCode(ev);
285
+ const uc = this._getUnicodeChar(ev);
286
+ const kd = isKeyDown ? 1 : 0;
287
+ const cs = this._getControlKeyState(ev);
288
+ const rc = 1; // Repeat count, always 1 for now
289
+
290
+ // Format: CSI Vk ; Sc ; Uc ; Kd ; Cs ; Rc _
291
+ return {
292
+ type: KeyboardResultType.SEND_KEY,
293
+ cancel: true,
294
+ key: `${C0.ESC}[${vk};${sc};${uc};${kd};${cs};${rc}_`
295
+ };
296
+ }
297
+ }
@@ -4,35 +4,35 @@
4
4
  * @license MIT
5
5
  */
6
6
 
7
- import { Disposable } from 'vs/base/common/lifecycle';
8
- import { Emitter } from 'vs/base/common/event';
7
+ import { Disposable } from 'common/Lifecycle';
8
+ import { Emitter } from 'common/Event';
9
9
 
10
10
  declare const setTimeout: (handler: () => void, timeout?: number) => void;
11
11
 
12
- /**
13
- * Safety watermark to avoid memory exhaustion and browser engine crash on fast data input.
14
- * Enable flow control to avoid this limit and make sure that your backend correctly
15
- * propagates this to the underlying pty. (see docs for further instructions)
16
- * Since this limit is meant as a safety parachute to prevent browser crashs,
17
- * it is set to a very high number. Typically xterm.js gets unresponsive with
18
- * a 100 times lower number (>500 kB).
19
- */
20
- const DISCARD_WATERMARK = 50000000; // ~50 MB
21
-
22
- /**
23
- * The max number of ms to spend on writes before allowing the renderer to
24
- * catch up with a 0ms setTimeout. A value of < 33 to keep us close to
25
- * 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS
26
- * depends on the time it takes for the renderer to draw the frame.
27
- */
28
- const WRITE_TIMEOUT_MS = 12;
29
-
30
- /**
31
- * Threshold of max held chunks in the write buffer, that were already processed.
32
- * This is a tradeoff between extensive write buffer shifts (bad runtime) and high
33
- * memory consumption by data thats not used anymore.
34
- */
35
- const WRITE_BUFFER_LENGTH_THRESHOLD = 50;
12
+ const enum Constants {
13
+ /**
14
+ * Safety watermark to avoid memory exhaustion and browser engine crash on fast data input.
15
+ * Enable flow control to avoid this limit and make sure that your backend correctly
16
+ * propagates this to the underlying pty. (see docs for further instructions)
17
+ * Since this limit is meant as a safety parachute to prevent browser crashs,
18
+ * it is set to a very high number. Typically xterm.js gets unresponsive with
19
+ * a 100 times lower number (>500 kB).
20
+ */
21
+ DISCARD_WATERMARK = 50000000, // ~50 MB
22
+ /**
23
+ * The max number of ms to spend on writes before allowing the renderer to
24
+ * catch up with a 0ms setTimeout. A value of < 33 to keep us close to
25
+ * 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS
26
+ * depends on the time it takes for the renderer to draw the frame.
27
+ */
28
+ WRITE_TIMEOUT_MS = 12,
29
+ /**
30
+ * Threshold of max held chunks in the write buffer, that were already processed.
31
+ * This is a tradeoff between extensive write buffer shifts (bad runtime) and high
32
+ * memory consumption by data thats not used anymore.
33
+ */
34
+ WRITE_BUFFER_LENGTH_THRESHOLD = 50
35
+ }
36
36
 
37
37
  export class WriteBuffer extends Disposable {
38
38
  private _writeBuffer: (string | Uint8Array)[] = [];
@@ -54,6 +54,38 @@ export class WriteBuffer extends Disposable {
54
54
  this._didUserInput = true;
55
55
  }
56
56
 
57
+ /**
58
+ * Flushes all pending writes synchronously. This is useful when you need to
59
+ * ensure all queued data is processed before performing an operation that
60
+ * depends upon everything being parsed like resize.
61
+ *
62
+ * Note: This is unreliable with async parser handlers as it does not wait for
63
+ * promises to resolve.
64
+ */
65
+ public flushSync(): void {
66
+ // exit early if another sync write loop is active
67
+ if (this._isSyncWriting) {
68
+ return;
69
+ }
70
+ this._isSyncWriting = true;
71
+
72
+ // Process all pending chunks synchronously
73
+ let chunk: string | Uint8Array | undefined;
74
+ while (chunk = this._writeBuffer.shift()) {
75
+ this._action(chunk);
76
+ const cb = this._callbacks.shift();
77
+ if (cb) cb();
78
+ }
79
+
80
+ // Reset buffer state
81
+ this._pendingData = 0;
82
+ this._bufferOffset = 0x7FFFFFFF;
83
+ this._writeBuffer.length = 0;
84
+ this._callbacks.length = 0;
85
+
86
+ this._isSyncWriting = false;
87
+ }
88
+
57
89
  /**
58
90
  * @deprecated Unreliable, to be removed soon.
59
91
  */
@@ -101,7 +133,7 @@ export class WriteBuffer extends Disposable {
101
133
  }
102
134
 
103
135
  public write(data: string | Uint8Array, callback?: () => void): void {
104
- if (this._pendingData > DISCARD_WATERMARK) {
136
+ if (this._pendingData > Constants.DISCARD_WATERMARK) {
105
137
  throw new Error('write data discarded, use flow control to avoid losing data');
106
138
  }
107
139
 
@@ -137,7 +169,7 @@ export class WriteBuffer extends Disposable {
137
169
  * effectively lowering the redrawing needs, schematically:
138
170
  *
139
171
  * macroTask _innerWrite:
140
- * if (performance.now() - (lastTime | 0) < WRITE_TIMEOUT_MS):
172
+ * if (performance.now() - (lastTime | 0) < Constants.WRITE_TIMEOUT_MS):
141
173
  * schedule microTask _innerWrite(lastTime)
142
174
  * else:
143
175
  * schedule macroTask _innerWrite(0)
@@ -186,7 +218,7 @@ export class WriteBuffer extends Disposable {
186
218
  * responsibility to slice hard work), but we can at least schedule a screen update as we
187
219
  * gain control.
188
220
  */
189
- const continuation: (r: boolean) => void = (r: boolean) => performance.now() - startTime >= WRITE_TIMEOUT_MS
221
+ const continuation: (r: boolean) => void = (r: boolean) => performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS
190
222
  ? setTimeout(() => this._innerWrite(0, r))
191
223
  : this._innerWrite(startTime, r);
192
224
 
@@ -203,7 +235,7 @@ export class WriteBuffer extends Disposable {
203
235
  * current microtask queue (executed before setTimeout).
204
236
  */
205
237
  // const continuation: (r: boolean) => void = performance.now() - startTime >=
206
- // WRITE_TIMEOUT_MS
238
+ // Constants.WRITE_TIMEOUT_MS
207
239
  // ? r => setTimeout(() => this._innerWrite(0, r))
208
240
  // : r => this._innerWrite(startTime, r);
209
241
 
@@ -223,14 +255,14 @@ export class WriteBuffer extends Disposable {
223
255
  this._bufferOffset++;
224
256
  this._pendingData -= data.length;
225
257
 
226
- if (performance.now() - startTime >= WRITE_TIMEOUT_MS) {
258
+ if (performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS) {
227
259
  break;
228
260
  }
229
261
  }
230
262
  if (this._writeBuffer.length > this._bufferOffset) {
231
263
  // Allow renderer to catch up before processing the next batch
232
264
  // trim already processed chunks if we are above threshold
233
- if (this._bufferOffset > WRITE_BUFFER_LENGTH_THRESHOLD) {
265
+ if (this._bufferOffset > Constants.WRITE_BUFFER_LENGTH_THRESHOLD) {
234
266
  this._writeBuffer = this._writeBuffer.slice(this._bufferOffset);
235
267
  this._callbacks = this._callbacks.slice(this._bufferOffset);
236
268
  this._bufferOffset = 0;
@@ -24,7 +24,7 @@ export function parseColor(data: string): [number, number, number] | undefined {
24
24
  if (!data) return;
25
25
  // also handle uppercases
26
26
  let low = data.toLowerCase();
27
- if (low.indexOf('rgb:') === 0) {
27
+ if (low.startsWith('rgb:')) {
28
28
  // 'rgb:' specifier
29
29
  low = low.slice(4);
30
30
  const m = RGB_REX.exec(low);
@@ -36,7 +36,7 @@ export function parseColor(data: string): [number, number, number] | undefined {
36
36
  Math.round(parseInt(m[3] || m[6] || m[9] || m[12], 16) / base * 255)
37
37
  ];
38
38
  }
39
- } else if (low.indexOf('#') === 0) {
39
+ } else if (low.startsWith('#')) {
40
40
  // '#' specifier
41
41
  low = low.slice(1);
42
42
  if (HASH_REX.exec(low) && [3, 6, 9, 12].includes(low.length)) {
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Copyright (c) 2025 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { IApcHandler, IHandlerCollection, ApcFallbackHandlerType, IApcParser, ISubParserStackState } from 'common/parser/Types';
7
+ import { ParserConstants } from 'common/parser/Constants';
8
+ import { utf32ToString } from 'common/input/TextDecoder';
9
+ import { IDisposable } from 'common/Types';
10
+
11
+ const EMPTY_HANDLERS: IApcHandler[] = [];
12
+
13
+ /**
14
+ * APC Parser for handling Application Program Command sequences.
15
+ * APC sequences use the format: ESC _ <identifier><data> ESC \
16
+ *
17
+ * Unlike OSC which uses numeric identifiers (e.g., OSC 1337),
18
+ * APC uses the first character as the identifier (e.g., 'G' for Kitty graphics).
19
+ * The identifier is the character code of the first byte after ESC _.
20
+ */
21
+ export class ApcParser implements IApcParser {
22
+ private _handlers: IHandlerCollection<IApcHandler> = Object.create(null);
23
+ private _active = EMPTY_HANDLERS;
24
+ private _ident: number = 0;
25
+ private _handlerFb: ApcFallbackHandlerType = () => { };
26
+ private _stack: ISubParserStackState = {
27
+ paused: false,
28
+ loopPosition: 0,
29
+ fallThrough: false
30
+ };
31
+
32
+ /**
33
+ * Register an APC handler for a specific identifier.
34
+ * @param ident The character code of the first byte (e.g., 0x47 for 'G')
35
+ * @param handler The handler to register
36
+ */
37
+ public registerHandler(ident: number, handler: IApcHandler): IDisposable {
38
+ this._handlers[ident] ??= [];
39
+ const handlerList = this._handlers[ident];
40
+ handlerList.push(handler);
41
+ return {
42
+ dispose: () => {
43
+ const handlerIndex = handlerList.indexOf(handler);
44
+ if (handlerIndex !== -1) {
45
+ handlerList.splice(handlerIndex, 1);
46
+ }
47
+ }
48
+ };
49
+ }
50
+
51
+ public clearHandler(ident: number): void {
52
+ if (this._handlers[ident]) delete this._handlers[ident];
53
+ }
54
+
55
+ public setHandlerFallback(handler: ApcFallbackHandlerType): void {
56
+ this._handlerFb = handler;
57
+ }
58
+
59
+ public dispose(): void {
60
+ this._handlers = Object.create(null);
61
+ this._handlerFb = () => { };
62
+ this._active = EMPTY_HANDLERS;
63
+ }
64
+
65
+ public reset(): void {
66
+ // force cleanup handlers
67
+ if (this._active.length) {
68
+ for (let j = this._stack.paused ? this._stack.loopPosition - 1 : this._active.length - 1; j >= 0; --j) {
69
+ this._active[j].end(false);
70
+ }
71
+ }
72
+ this._stack.paused = false;
73
+ this._active = EMPTY_HANDLERS;
74
+ this._ident = 0;
75
+ }
76
+
77
+ public start(ident: number): void {
78
+ // always reset leftover handlers
79
+ this.reset();
80
+ this._ident = ident;
81
+ this._active = this._handlers[ident] || EMPTY_HANDLERS;
82
+ if (!this._active.length) {
83
+ this._handlerFb(this._ident, 'START');
84
+ } else {
85
+ for (let j = this._active.length - 1; j >= 0; j--) {
86
+ this._active[j].start();
87
+ }
88
+ }
89
+ }
90
+
91
+ public put(data: Uint32Array, start: number, end: number): void {
92
+ if (!this._active.length) {
93
+ this._handlerFb(this._ident, 'PUT', utf32ToString(data, start, end));
94
+ } else {
95
+ for (let j = this._active.length - 1; j >= 0; j--) {
96
+ this._active[j].put(data, start, end);
97
+ }
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Indicates end of an APC command.
103
+ * Whether the APC got aborted or finished normally
104
+ * is indicated by `success`.
105
+ */
106
+ public end(success: boolean, promiseResult: boolean = true): void | Promise<boolean> {
107
+ if (!this._active.length) {
108
+ this._handlerFb(this._ident, 'END', success);
109
+ } else {
110
+ let handlerResult: boolean | Promise<boolean> = false;
111
+ let j = this._active.length - 1;
112
+ let fallThrough = false;
113
+ if (this._stack.paused) {
114
+ j = this._stack.loopPosition - 1;
115
+ handlerResult = promiseResult;
116
+ fallThrough = this._stack.fallThrough;
117
+ this._stack.paused = false;
118
+ }
119
+ if (!fallThrough && handlerResult === false) {
120
+ for (; j >= 0; j--) {
121
+ handlerResult = this._active[j].end(success);
122
+ if (handlerResult === true) {
123
+ break;
124
+ } else if (handlerResult instanceof Promise) {
125
+ this._stack.paused = true;
126
+ this._stack.loopPosition = j;
127
+ this._stack.fallThrough = false;
128
+ return handlerResult;
129
+ }
130
+ }
131
+ j--;
132
+ }
133
+ // cleanup left over handlers (fallThrough for async)
134
+ for (; j >= 0; j--) {
135
+ handlerResult = this._active[j].end(false);
136
+ if (handlerResult instanceof Promise) {
137
+ this._stack.paused = true;
138
+ this._stack.loopPosition = j;
139
+ this._stack.fallThrough = true;
140
+ return handlerResult;
141
+ }
142
+ }
143
+ }
144
+ this._active = EMPTY_HANDLERS;
145
+ this._ident = 0;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Convenient class to allow attaching string based handler functions
151
+ * as APC handlers.
152
+ */
153
+ export class ApcHandler implements IApcHandler {
154
+ private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT;
155
+
156
+ private _data = '';
157
+ private _hitLimit: boolean = false;
158
+
159
+ constructor(private _handler: (data: string) => boolean | Promise<boolean>) { }
160
+
161
+ public start(): void {
162
+ this._data = '';
163
+ this._hitLimit = false;
164
+ }
165
+
166
+ public put(data: Uint32Array, start: number, end: number): void {
167
+ if (this._hitLimit) {
168
+ return;
169
+ }
170
+ this._data += utf32ToString(data, start, end);
171
+ if (this._data.length > ApcHandler._payloadLimit) {
172
+ this._data = '';
173
+ this._hitLimit = true;
174
+ }
175
+ }
176
+
177
+ public end(success: boolean): boolean | Promise<boolean> {
178
+ let ret: boolean | Promise<boolean> = false;
179
+ if (this._hitLimit) {
180
+ ret = false;
181
+ } else if (success) {
182
+ ret = this._handler(this._data);
183
+ if (ret instanceof Promise) {
184
+ // need to hold data until `ret` got resolved
185
+ // dont care for errors, data will be freed anyway on next start
186
+ return ret.then(res => {
187
+ this._data = '';
188
+ this._hitLimit = false;
189
+ return res;
190
+ });
191
+ }
192
+ }
193
+ this._data = '';
194
+ this._hitLimit = false;
195
+ return ret;
196
+ }
197
+ }