@xterm/xterm 6.1.0-beta.21 → 6.1.0-beta.211

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 (158) hide show
  1. package/README.md +61 -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 +24 -13
  8. package/src/browser/AccessibilityManager.ts +6 -3
  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 +205 -41
  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 +579 -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 +16 -8
  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 +28 -18
  56. package/src/common/Event.ts +118 -0
  57. package/src/common/InputHandler.ts +263 -43
  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 +20 -14
  65. package/src/common/buffer/BufferLine.ts +4 -5
  66. package/src/common/buffer/BufferSet.ts +7 -6
  67. package/src/common/buffer/CellData.ts +57 -0
  68. package/src/common/buffer/Marker.ts +2 -2
  69. package/src/common/buffer/Types.ts +6 -2
  70. package/src/common/data/EscapeSequences.ts +71 -70
  71. package/src/common/input/Keyboard.ts +14 -7
  72. package/src/common/input/KittyKeyboard.ts +519 -0
  73. package/src/common/input/Win32InputMode.ts +297 -0
  74. package/src/common/input/WriteBuffer.ts +34 -2
  75. package/src/common/input/XParseColor.ts +2 -2
  76. package/src/common/parser/ApcParser.ts +245 -0
  77. package/src/common/parser/Constants.ts +22 -4
  78. package/src/common/parser/DcsParser.ts +5 -5
  79. package/src/common/parser/EscapeSequenceParser.ts +167 -57
  80. package/src/common/parser/OscParser.ts +5 -5
  81. package/src/common/parser/Params.ts +13 -0
  82. package/src/common/parser/Types.ts +36 -2
  83. package/src/common/public/BufferLineApiView.ts +2 -2
  84. package/src/common/public/BufferNamespaceApi.ts +2 -2
  85. package/src/common/public/ParserApi.ts +3 -0
  86. package/src/common/services/BufferService.ts +8 -5
  87. package/src/common/services/CharsetService.ts +4 -0
  88. package/src/common/services/CoreService.ts +18 -4
  89. package/src/common/services/DecorationService.ts +24 -8
  90. package/src/common/services/LogService.ts +1 -31
  91. package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +21 -132
  92. package/src/common/services/OptionsService.ts +13 -4
  93. package/src/common/services/Services.ts +47 -40
  94. package/src/common/services/UnicodeService.ts +1 -1
  95. package/typings/xterm.d.ts +316 -32
  96. package/src/common/TypedArrayUtils.ts +0 -17
  97. package/src/vs/base/browser/browser.ts +0 -141
  98. package/src/vs/base/browser/canIUse.ts +0 -49
  99. package/src/vs/base/browser/dom.ts +0 -2369
  100. package/src/vs/base/browser/fastDomNode.ts +0 -316
  101. package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
  102. package/src/vs/base/browser/iframe.ts +0 -135
  103. package/src/vs/base/browser/keyboardEvent.ts +0 -213
  104. package/src/vs/base/browser/mouseEvent.ts +0 -229
  105. package/src/vs/base/browser/touch.ts +0 -372
  106. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
  107. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
  108. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
  109. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
  110. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
  111. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
  112. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
  113. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
  114. package/src/vs/base/browser/ui/widget.ts +0 -57
  115. package/src/vs/base/browser/window.ts +0 -14
  116. package/src/vs/base/common/arrays.ts +0 -887
  117. package/src/vs/base/common/arraysFind.ts +0 -202
  118. package/src/vs/base/common/assert.ts +0 -71
  119. package/src/vs/base/common/async.ts +0 -1992
  120. package/src/vs/base/common/cancellation.ts +0 -148
  121. package/src/vs/base/common/charCode.ts +0 -450
  122. package/src/vs/base/common/collections.ts +0 -140
  123. package/src/vs/base/common/decorators.ts +0 -130
  124. package/src/vs/base/common/equals.ts +0 -146
  125. package/src/vs/base/common/errors.ts +0 -303
  126. package/src/vs/base/common/event.ts +0 -1778
  127. package/src/vs/base/common/functional.ts +0 -32
  128. package/src/vs/base/common/hash.ts +0 -316
  129. package/src/vs/base/common/iterator.ts +0 -159
  130. package/src/vs/base/common/keyCodes.ts +0 -526
  131. package/src/vs/base/common/keybindings.ts +0 -284
  132. package/src/vs/base/common/lazy.ts +0 -47
  133. package/src/vs/base/common/lifecycle.ts +0 -801
  134. package/src/vs/base/common/linkedList.ts +0 -142
  135. package/src/vs/base/common/map.ts +0 -202
  136. package/src/vs/base/common/numbers.ts +0 -98
  137. package/src/vs/base/common/observable.ts +0 -76
  138. package/src/vs/base/common/observableInternal/api.ts +0 -31
  139. package/src/vs/base/common/observableInternal/autorun.ts +0 -281
  140. package/src/vs/base/common/observableInternal/base.ts +0 -489
  141. package/src/vs/base/common/observableInternal/debugName.ts +0 -145
  142. package/src/vs/base/common/observableInternal/derived.ts +0 -428
  143. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
  144. package/src/vs/base/common/observableInternal/logging.ts +0 -328
  145. package/src/vs/base/common/observableInternal/promise.ts +0 -209
  146. package/src/vs/base/common/observableInternal/utils.ts +0 -610
  147. package/src/vs/base/common/platform.ts +0 -281
  148. package/src/vs/base/common/scrollable.ts +0 -522
  149. package/src/vs/base/common/sequence.ts +0 -34
  150. package/src/vs/base/common/stopwatch.ts +0 -43
  151. package/src/vs/base/common/strings.ts +0 -557
  152. package/src/vs/base/common/symbols.ts +0 -9
  153. package/src/vs/base/common/uint.ts +0 -59
  154. package/src/vs/patches/nls.ts +0 -90
  155. package/src/vs/typings/base-common.d.ts +0 -20
  156. package/src/vs/typings/require.d.ts +0 -42
  157. package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
  158. 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,8 +4,8 @@
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
 
@@ -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
  */
@@ -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,245 @@
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 { ApcState, 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 _state = ApcState.START;
23
+ private _active = EMPTY_HANDLERS;
24
+ private _id = -1;
25
+ private _handlers: IHandlerCollection<IApcHandler> = Object.create(null);
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 if payload was already sent
68
+ if (this._state === ApcState.PAYLOAD) {
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._id = -1;
76
+ this._state = ApcState.START;
77
+ }
78
+
79
+ private _start(): void {
80
+ this._active = this._handlers[this._id] || EMPTY_HANDLERS;
81
+ if (!this._active.length) {
82
+ this._handlerFb(this._id, 'START');
83
+ } else {
84
+ for (let j = this._active.length - 1; j >= 0; j--) {
85
+ this._active[j].start();
86
+ }
87
+ }
88
+ }
89
+
90
+ private _put(data: Uint32Array, start: number, end: number): void {
91
+ if (!this._active.length) {
92
+ this._handlerFb(this._id, 'PUT', utf32ToString(data, start, end));
93
+ } else {
94
+ for (let j = this._active.length - 1; j >= 0; j--) {
95
+ this._active[j].put(data, start, end);
96
+ }
97
+ }
98
+ }
99
+
100
+ public start(): void {
101
+ // always reset leftover handlers
102
+ this.reset();
103
+ this._state = ApcState.ID;
104
+ }
105
+
106
+ /**
107
+ * Put data to current APC command.
108
+ * For APC, the first character is used as the identifier.
109
+ * Format: ESC _ <identifier><payload> ESC \
110
+ * Example: ESC _ G f=100,a=T;... ESC \ (Kitty graphics, identifier='G')
111
+ */
112
+ public put(data: Uint32Array, start: number, end: number): void {
113
+ if (this._state === ApcState.ABORT) {
114
+ return;
115
+ }
116
+ if (this._state === ApcState.ID) {
117
+ // The first character is the identifier
118
+ if (start < end) {
119
+ this._id = data[start++];
120
+ this._state = ApcState.PAYLOAD;
121
+ this._start();
122
+ }
123
+ }
124
+ if (this._state === ApcState.PAYLOAD && end - start > 0) {
125
+ this._put(data, start, end);
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Indicates end of an APC command.
131
+ * Whether the APC got aborted or finished normally
132
+ * is indicated by `success`.
133
+ */
134
+ public end(success: boolean, promiseResult: boolean = true): void | Promise<boolean> {
135
+ if (this._state === ApcState.START) {
136
+ return;
137
+ }
138
+ // do nothing if command was faulty
139
+ if (this._state !== ApcState.ABORT) {
140
+ // if we are still in ID state and get an early end
141
+ // means we got an empty APC sequence with no identifier,
142
+ // which is invalid - just reset and return
143
+ if (this._state === ApcState.ID) {
144
+ this._active = EMPTY_HANDLERS;
145
+ this._id = -1;
146
+ this._state = ApcState.START;
147
+ return;
148
+ }
149
+
150
+ if (!this._active.length) {
151
+ this._handlerFb(this._id, 'END', success);
152
+ } else {
153
+ let handlerResult: boolean | Promise<boolean> = false;
154
+ let j = this._active.length - 1;
155
+ let fallThrough = false;
156
+ if (this._stack.paused) {
157
+ j = this._stack.loopPosition - 1;
158
+ handlerResult = promiseResult;
159
+ fallThrough = this._stack.fallThrough;
160
+ this._stack.paused = false;
161
+ }
162
+ if (!fallThrough && handlerResult === false) {
163
+ for (; j >= 0; j--) {
164
+ handlerResult = this._active[j].end(success);
165
+ if (handlerResult === true) {
166
+ break;
167
+ } else if (handlerResult instanceof Promise) {
168
+ this._stack.paused = true;
169
+ this._stack.loopPosition = j;
170
+ this._stack.fallThrough = false;
171
+ return handlerResult;
172
+ }
173
+ }
174
+ j--;
175
+ }
176
+ // cleanup left over handlers
177
+ // we always have to call .end for proper cleanup,
178
+ // here we use `success` to indicate whether a handler should execute
179
+ for (; j >= 0; j--) {
180
+ handlerResult = this._active[j].end(false);
181
+ if (handlerResult instanceof Promise) {
182
+ this._stack.paused = true;
183
+ this._stack.loopPosition = j;
184
+ this._stack.fallThrough = true;
185
+ return handlerResult;
186
+ }
187
+ }
188
+ }
189
+
190
+ }
191
+ this._active = EMPTY_HANDLERS;
192
+ this._id = -1;
193
+ this._state = ApcState.START;
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Convenient class to allow attaching string based handler functions
199
+ * as APC handlers.
200
+ */
201
+ export class ApcHandler implements IApcHandler {
202
+ private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT;
203
+
204
+ private _data = '';
205
+ private _hitLimit: boolean = false;
206
+
207
+ constructor(private _handler: (data: string) => boolean | Promise<boolean>) { }
208
+
209
+ public start(): void {
210
+ this._data = '';
211
+ this._hitLimit = false;
212
+ }
213
+
214
+ public put(data: Uint32Array, start: number, end: number): void {
215
+ if (this._hitLimit) {
216
+ return;
217
+ }
218
+ this._data += utf32ToString(data, start, end);
219
+ if (this._data.length > ApcHandler._payloadLimit) {
220
+ this._data = '';
221
+ this._hitLimit = true;
222
+ }
223
+ }
224
+
225
+ public end(success: boolean): boolean | Promise<boolean> {
226
+ let ret: boolean | Promise<boolean> = false;
227
+ if (this._hitLimit) {
228
+ ret = false;
229
+ } else if (success) {
230
+ ret = this._handler(this._data);
231
+ if (ret instanceof Promise) {
232
+ // need to hold data until `ret` got resolved
233
+ // dont care for errors, data will be freed anyway on next start
234
+ return ret.then(res => {
235
+ this._data = '';
236
+ this._hitLimit = false;
237
+ return res;
238
+ });
239
+ }
240
+ }
241
+ this._data = '';
242
+ this._hitLimit = false;
243
+ return ret;
244
+ }
245
+ }
@@ -14,13 +14,16 @@ export const enum ParserState {
14
14
  CSI_PARAM = 4,
15
15
  CSI_INTERMEDIATE = 5,
16
16
  CSI_IGNORE = 6,
17
- SOS_PM_APC_STRING = 7,
17
+ SOS_PM_STRING = 7,
18
18
  OSC_STRING = 8,
19
19
  DCS_ENTRY = 9,
20
20
  DCS_PARAM = 10,
21
21
  DCS_IGNORE = 11,
22
22
  DCS_INTERMEDIATE = 12,
23
- DCS_PASSTHROUGH = 13
23
+ DCS_PASSTHROUGH = 13,
24
+ APC_STRING = 14,
25
+ // Number of states, meaning LAST_STATE + 1.
26
+ STATE_LENGTH = 15
24
27
  }
25
28
 
26
29
  /**
@@ -41,7 +44,10 @@ export const enum ParserAction {
41
44
  CLEAR = 11,
42
45
  DCS_HOOK = 12,
43
46
  DCS_PUT = 13,
44
- DCS_UNHOOK = 14
47
+ DCS_UNHOOK = 14,
48
+ APC_START = 15,
49
+ APC_PUT = 16,
50
+ APC_END = 17
45
51
  }
46
52
 
47
53
  /**
@@ -54,5 +60,17 @@ export const enum OscState {
54
60
  ABORT = 3
55
61
  }
56
62
 
63
+ /**
64
+ * Internal states of ApcParser.
65
+ */
66
+ export const enum ApcState {
67
+ START = 0,
68
+ ID = 1,
69
+ PAYLOAD = 2,
70
+ ABORT = 3
71
+ }
72
+
57
73
  // payload limit for OSC and DCS
58
- export const PAYLOAD_LIMIT = 10000000;
74
+ export const enum ParserConstants {
75
+ PAYLOAD_LIMIT = 10000000
76
+ }
@@ -7,7 +7,7 @@ import { IDisposable } from 'common/Types';
7
7
  import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandlerType, ISubParserStackState } from 'common/parser/Types';
8
8
  import { utf32ToString } from 'common/input/TextDecoder';
9
9
  import { Params } from 'common/parser/Params';
10
- import { PAYLOAD_LIMIT } from 'common/parser/Constants';
10
+ import { ParserConstants } from 'common/parser/Constants';
11
11
 
12
12
  const EMPTY_HANDLERS: IDcsHandler[] = [];
13
13
 
@@ -29,9 +29,7 @@ export class DcsParser implements IDcsParser {
29
29
  }
30
30
 
31
31
  public registerHandler(ident: number, handler: IDcsHandler): IDisposable {
32
- if (this._handlers[ident] === undefined) {
33
- this._handlers[ident] = [];
34
- }
32
+ this._handlers[ident] ??= [];
35
33
  const handlerList = this._handlers[ident];
36
34
  handlerList.push(handler);
37
35
  return {
@@ -140,6 +138,8 @@ EMPTY_PARAMS.addParam(0);
140
138
  * Note: The payload is currently limited to 50 MB (hardcoded).
141
139
  */
142
140
  export class DcsHandler implements IDcsHandler {
141
+ private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT;
142
+
143
143
  private _data = '';
144
144
  private _params: IParams = EMPTY_PARAMS;
145
145
  private _hitLimit: boolean = false;
@@ -161,7 +161,7 @@ export class DcsHandler implements IDcsHandler {
161
161
  return;
162
162
  }
163
163
  this._data += utf32ToString(data, start, end);
164
- if (this._data.length > PAYLOAD_LIMIT) {
164
+ if (this._data.length > DcsHandler._payloadLimit) {
165
165
  this._data = '';
166
166
  this._hitLimit = true;
167
167
  }