@xterm/xterm 6.1.0-beta.24 → 6.1.0-beta.240

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 (162) 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 +149 -319
  11. package/src/browser/Dom.ts +178 -0
  12. package/src/browser/Linkifier.ts +11 -11
  13. package/src/browser/OscLinkProvider.ts +82 -14
  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 +28 -16
  49. package/src/browser/services/SelectionService.ts +45 -39
  50. package/src/browser/services/Services.ts +40 -17
  51. package/src/browser/services/ThemeService.ts +2 -2
  52. package/src/common/Async.ts +141 -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 -85
  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/StringBuilder.ts +67 -0
  62. package/src/common/TaskQueue.ts +14 -5
  63. package/src/common/Types.ts +51 -31
  64. package/src/common/Version.ts +9 -0
  65. package/src/common/buffer/Buffer.ts +34 -19
  66. package/src/common/buffer/BufferLine.ts +133 -65
  67. package/src/common/buffer/BufferLineStringCache.ts +69 -0
  68. package/src/common/buffer/BufferSet.ts +11 -6
  69. package/src/common/buffer/CellData.ts +57 -0
  70. package/src/common/buffer/Marker.ts +2 -2
  71. package/src/common/buffer/Types.ts +6 -2
  72. package/src/common/data/EscapeSequences.ts +71 -70
  73. package/src/common/input/Keyboard.ts +14 -7
  74. package/src/common/input/KittyKeyboard.ts +526 -0
  75. package/src/common/input/Win32InputMode.ts +297 -0
  76. package/src/common/input/WriteBuffer.ts +69 -32
  77. package/src/common/input/XParseColor.ts +2 -2
  78. package/src/common/parser/ApcParser.ts +196 -0
  79. package/src/common/parser/Constants.ts +14 -4
  80. package/src/common/parser/DcsParser.ts +11 -12
  81. package/src/common/parser/EscapeSequenceParser.ts +205 -63
  82. package/src/common/parser/OscParser.ts +11 -12
  83. package/src/common/parser/Params.ts +27 -8
  84. package/src/common/parser/Types.ts +36 -2
  85. package/src/common/public/BufferLineApiView.ts +2 -2
  86. package/src/common/public/BufferNamespaceApi.ts +3 -3
  87. package/src/common/public/ParserApi.ts +3 -0
  88. package/src/common/services/BufferService.ts +14 -9
  89. package/src/common/services/CharsetService.ts +4 -0
  90. package/src/common/services/CoreService.ts +22 -9
  91. package/src/common/services/DecorationService.ts +258 -8
  92. package/src/common/services/LogService.ts +1 -31
  93. package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +21 -132
  94. package/src/common/services/OptionsService.ts +13 -4
  95. package/src/common/services/ServiceRegistry.ts +9 -7
  96. package/src/common/services/Services.ts +49 -40
  97. package/src/common/services/UnicodeService.ts +1 -1
  98. package/typings/xterm.d.ts +318 -34
  99. package/src/common/Clone.ts +0 -23
  100. package/src/common/TypedArrayUtils.ts +0 -17
  101. package/src/vs/base/browser/browser.ts +0 -141
  102. package/src/vs/base/browser/canIUse.ts +0 -49
  103. package/src/vs/base/browser/dom.ts +0 -2369
  104. package/src/vs/base/browser/fastDomNode.ts +0 -316
  105. package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
  106. package/src/vs/base/browser/iframe.ts +0 -135
  107. package/src/vs/base/browser/keyboardEvent.ts +0 -213
  108. package/src/vs/base/browser/mouseEvent.ts +0 -229
  109. package/src/vs/base/browser/touch.ts +0 -372
  110. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
  111. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
  112. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
  113. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
  114. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
  115. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
  116. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
  117. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
  118. package/src/vs/base/browser/ui/widget.ts +0 -57
  119. package/src/vs/base/browser/window.ts +0 -14
  120. package/src/vs/base/common/arrays.ts +0 -887
  121. package/src/vs/base/common/arraysFind.ts +0 -202
  122. package/src/vs/base/common/assert.ts +0 -71
  123. package/src/vs/base/common/async.ts +0 -1992
  124. package/src/vs/base/common/cancellation.ts +0 -148
  125. package/src/vs/base/common/charCode.ts +0 -450
  126. package/src/vs/base/common/collections.ts +0 -140
  127. package/src/vs/base/common/decorators.ts +0 -130
  128. package/src/vs/base/common/equals.ts +0 -146
  129. package/src/vs/base/common/errors.ts +0 -303
  130. package/src/vs/base/common/event.ts +0 -1778
  131. package/src/vs/base/common/functional.ts +0 -32
  132. package/src/vs/base/common/hash.ts +0 -316
  133. package/src/vs/base/common/iterator.ts +0 -159
  134. package/src/vs/base/common/keyCodes.ts +0 -526
  135. package/src/vs/base/common/keybindings.ts +0 -284
  136. package/src/vs/base/common/lazy.ts +0 -47
  137. package/src/vs/base/common/lifecycle.ts +0 -801
  138. package/src/vs/base/common/linkedList.ts +0 -142
  139. package/src/vs/base/common/map.ts +0 -202
  140. package/src/vs/base/common/numbers.ts +0 -98
  141. package/src/vs/base/common/observable.ts +0 -76
  142. package/src/vs/base/common/observableInternal/api.ts +0 -31
  143. package/src/vs/base/common/observableInternal/autorun.ts +0 -281
  144. package/src/vs/base/common/observableInternal/base.ts +0 -489
  145. package/src/vs/base/common/observableInternal/debugName.ts +0 -145
  146. package/src/vs/base/common/observableInternal/derived.ts +0 -428
  147. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
  148. package/src/vs/base/common/observableInternal/logging.ts +0 -328
  149. package/src/vs/base/common/observableInternal/promise.ts +0 -209
  150. package/src/vs/base/common/observableInternal/utils.ts +0 -610
  151. package/src/vs/base/common/platform.ts +0 -281
  152. package/src/vs/base/common/scrollable.ts +0 -522
  153. package/src/vs/base/common/sequence.ts +0 -34
  154. package/src/vs/base/common/stopwatch.ts +0 -43
  155. package/src/vs/base/common/strings.ts +0 -557
  156. package/src/vs/base/common/symbols.ts +0 -9
  157. package/src/vs/base/common/uint.ts +0 -59
  158. package/src/vs/patches/nls.ts +0 -90
  159. package/src/vs/typings/base-common.d.ts +0 -20
  160. package/src/vs/typings/require.d.ts +0 -42
  161. package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
  162. 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,43 @@ 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
+ let didProcess = false;
75
+ while (chunk = this._writeBuffer.shift()) {
76
+ didProcess = true;
77
+ this._action(chunk);
78
+ const cb = this._callbacks.shift();
79
+ if (cb) cb();
80
+ }
81
+
82
+ // Reset buffer state
83
+ this._pendingData = 0;
84
+ this._bufferOffset = 0x7FFFFFFF;
85
+ this._writeBuffer.length = 0;
86
+ this._callbacks.length = 0;
87
+
88
+ this._isSyncWriting = false;
89
+ if (didProcess) {
90
+ this._onWriteParsed.fire();
91
+ }
92
+ }
93
+
57
94
  /**
58
95
  * @deprecated Unreliable, to be removed soon.
59
96
  */
@@ -101,7 +138,7 @@ export class WriteBuffer extends Disposable {
101
138
  }
102
139
 
103
140
  public write(data: string | Uint8Array, callback?: () => void): void {
104
- if (this._pendingData > DISCARD_WATERMARK) {
141
+ if (this._pendingData > Constants.DISCARD_WATERMARK) {
105
142
  throw new Error('write data discarded, use flow control to avoid losing data');
106
143
  }
107
144
 
@@ -137,7 +174,7 @@ export class WriteBuffer extends Disposable {
137
174
  * effectively lowering the redrawing needs, schematically:
138
175
  *
139
176
  * macroTask _innerWrite:
140
- * if (performance.now() - (lastTime | 0) < WRITE_TIMEOUT_MS):
177
+ * if (performance.now() - (lastTime | 0) < Constants.WRITE_TIMEOUT_MS):
141
178
  * schedule microTask _innerWrite(lastTime)
142
179
  * else:
143
180
  * schedule macroTask _innerWrite(0)
@@ -186,7 +223,7 @@ export class WriteBuffer extends Disposable {
186
223
  * responsibility to slice hard work), but we can at least schedule a screen update as we
187
224
  * gain control.
188
225
  */
189
- const continuation: (r: boolean) => void = (r: boolean) => performance.now() - startTime >= WRITE_TIMEOUT_MS
226
+ const continuation: (r: boolean) => void = (r: boolean) => performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS
190
227
  ? setTimeout(() => this._innerWrite(0, r))
191
228
  : this._innerWrite(startTime, r);
192
229
 
@@ -203,7 +240,7 @@ export class WriteBuffer extends Disposable {
203
240
  * current microtask queue (executed before setTimeout).
204
241
  */
205
242
  // const continuation: (r: boolean) => void = performance.now() - startTime >=
206
- // WRITE_TIMEOUT_MS
243
+ // Constants.WRITE_TIMEOUT_MS
207
244
  // ? r => setTimeout(() => this._innerWrite(0, r))
208
245
  // : r => this._innerWrite(startTime, r);
209
246
 
@@ -223,14 +260,14 @@ export class WriteBuffer extends Disposable {
223
260
  this._bufferOffset++;
224
261
  this._pendingData -= data.length;
225
262
 
226
- if (performance.now() - startTime >= WRITE_TIMEOUT_MS) {
263
+ if (performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS) {
227
264
  break;
228
265
  }
229
266
  }
230
267
  if (this._writeBuffer.length > this._bufferOffset) {
231
268
  // Allow renderer to catch up before processing the next batch
232
269
  // trim already processed chunks if we are above threshold
233
- if (this._bufferOffset > WRITE_BUFFER_LENGTH_THRESHOLD) {
270
+ if (this._bufferOffset > Constants.WRITE_BUFFER_LENGTH_THRESHOLD) {
234
271
  this._writeBuffer = this._writeBuffer.slice(this._bufferOffset);
235
272
  this._callbacks = this._callbacks.slice(this._bufferOffset);
236
273
  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,196 @@
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
+ import { LimitedStringBuilder } from 'common/StringBuilder';
11
+
12
+ const EMPTY_HANDLERS: IApcHandler[] = [];
13
+
14
+ /**
15
+ * APC Parser for handling Application Program Command sequences.
16
+ * APC sequences use the format: ESC _ <identifier><data> ESC \
17
+ *
18
+ * Unlike OSC which uses numeric identifiers (e.g., OSC 1337),
19
+ * APC uses the first character as the identifier (e.g., 'G' for Kitty graphics).
20
+ * The identifier is the character code of the first byte after ESC _.
21
+ */
22
+ export class ApcParser implements IApcParser {
23
+ private _handlers: IHandlerCollection<IApcHandler> = Object.create(null);
24
+ private _active = EMPTY_HANDLERS;
25
+ private _ident: number = 0;
26
+ private _handlerFb: ApcFallbackHandlerType = () => { };
27
+ private _stack: ISubParserStackState = {
28
+ paused: false,
29
+ loopPosition: 0,
30
+ fallThrough: false
31
+ };
32
+
33
+ /**
34
+ * Register an APC handler for a specific identifier.
35
+ * @param ident The character code of the first byte (e.g., 0x47 for 'G')
36
+ * @param handler The handler to register
37
+ */
38
+ public registerHandler(ident: number, handler: IApcHandler): IDisposable {
39
+ this._handlers[ident] ??= [];
40
+ const handlerList = this._handlers[ident];
41
+ handlerList.push(handler);
42
+ return {
43
+ dispose: () => {
44
+ const handlerIndex = handlerList.indexOf(handler);
45
+ if (handlerIndex !== -1) {
46
+ handlerList.splice(handlerIndex, 1);
47
+ }
48
+ }
49
+ };
50
+ }
51
+
52
+ public clearHandler(ident: number): void {
53
+ if (this._handlers[ident]) delete this._handlers[ident];
54
+ }
55
+
56
+ public setHandlerFallback(handler: ApcFallbackHandlerType): void {
57
+ this._handlerFb = handler;
58
+ }
59
+
60
+ public dispose(): void {
61
+ this._handlers = Object.create(null);
62
+ this._handlerFb = () => { };
63
+ this._active = EMPTY_HANDLERS;
64
+ }
65
+
66
+ public reset(): void {
67
+ // force cleanup handlers
68
+ if (this._active.length) {
69
+ for (let j = this._stack.paused ? this._stack.loopPosition - 1 : this._active.length - 1; j >= 0; --j) {
70
+ this._active[j].end(false);
71
+ }
72
+ }
73
+ this._stack.paused = false;
74
+ this._active = EMPTY_HANDLERS;
75
+ this._ident = 0;
76
+ }
77
+
78
+ public start(ident: number): void {
79
+ // always reset leftover handlers
80
+ this.reset();
81
+ this._ident = ident;
82
+ this._active = this._handlers[ident] || EMPTY_HANDLERS;
83
+ if (!this._active.length) {
84
+ this._handlerFb(this._ident, 'START');
85
+ } else {
86
+ for (let j = this._active.length - 1; j >= 0; j--) {
87
+ this._active[j].start();
88
+ }
89
+ }
90
+ }
91
+
92
+ public put(data: Uint32Array, start: number, end: number): void {
93
+ if (!this._active.length) {
94
+ this._handlerFb(this._ident, 'PUT', utf32ToString(data, start, end));
95
+ } else {
96
+ for (let j = this._active.length - 1; j >= 0; j--) {
97
+ this._active[j].put(data, start, end);
98
+ }
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Indicates end of an APC command.
104
+ * Whether the APC got aborted or finished normally
105
+ * is indicated by `success`.
106
+ */
107
+ public end(success: boolean, promiseResult: boolean = true): void | Promise<boolean> {
108
+ if (!this._active.length) {
109
+ this._handlerFb(this._ident, 'END', success);
110
+ } else {
111
+ let handlerResult: boolean | Promise<boolean> = false;
112
+ let j = this._active.length - 1;
113
+ let fallThrough = false;
114
+ if (this._stack.paused) {
115
+ j = this._stack.loopPosition - 1;
116
+ handlerResult = promiseResult;
117
+ fallThrough = this._stack.fallThrough;
118
+ this._stack.paused = false;
119
+ }
120
+ if (!fallThrough && handlerResult === false) {
121
+ for (; j >= 0; j--) {
122
+ handlerResult = this._active[j].end(success);
123
+ if (handlerResult === true) {
124
+ break;
125
+ } else if (handlerResult instanceof Promise) {
126
+ this._stack.paused = true;
127
+ this._stack.loopPosition = j;
128
+ this._stack.fallThrough = false;
129
+ return handlerResult;
130
+ }
131
+ }
132
+ j--;
133
+ }
134
+ // cleanup left over handlers (fallThrough for async)
135
+ for (; j >= 0; j--) {
136
+ handlerResult = this._active[j].end(false);
137
+ if (handlerResult instanceof Promise) {
138
+ this._stack.paused = true;
139
+ this._stack.loopPosition = j;
140
+ this._stack.fallThrough = true;
141
+ return handlerResult;
142
+ }
143
+ }
144
+ }
145
+ this._active = EMPTY_HANDLERS;
146
+ this._ident = 0;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Convenient class to allow attaching string based handler functions
152
+ * as APC handlers.
153
+ */
154
+ export class ApcHandler implements IApcHandler {
155
+ private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT;
156
+
157
+ private _data = new LimitedStringBuilder(ApcHandler._payloadLimit);
158
+ private _hitLimit: boolean = false;
159
+
160
+ constructor(private _handler: (data: string) => boolean | Promise<boolean>) { }
161
+
162
+ public start(): void {
163
+ this._data.reset();
164
+ this._hitLimit = false;
165
+ }
166
+
167
+ public put(data: Uint32Array, start: number, end: number): void {
168
+ if (this._hitLimit) {
169
+ return;
170
+ }
171
+ if (this._data.append(utf32ToString(data, start, end))) {
172
+ this._hitLimit = true;
173
+ }
174
+ }
175
+
176
+ public end(success: boolean): boolean | Promise<boolean> {
177
+ let ret: boolean | Promise<boolean> = false;
178
+ if (this._hitLimit) {
179
+ ret = false;
180
+ } else if (success) {
181
+ ret = this._handler(this._data.toString());
182
+ if (ret instanceof Promise) {
183
+ // need to hold data until `ret` got resolved
184
+ // dont care for errors, data will be freed anyway on next start
185
+ return ret.then(res => {
186
+ this._data.reset();
187
+ this._hitLimit = false;
188
+ return res;
189
+ });
190
+ }
191
+ }
192
+ this._data.reset();
193
+ this._hitLimit = false;
194
+ return ret;
195
+ }
196
+ }