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

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 (163) 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/BufferReflow.ts +4 -1
  69. package/src/common/buffer/BufferSet.ts +11 -6
  70. package/src/common/buffer/CellData.ts +57 -0
  71. package/src/common/buffer/Marker.ts +2 -2
  72. package/src/common/buffer/Types.ts +6 -2
  73. package/src/common/data/EscapeSequences.ts +71 -70
  74. package/src/common/input/Keyboard.ts +14 -7
  75. package/src/common/input/KittyKeyboard.ts +526 -0
  76. package/src/common/input/Win32InputMode.ts +297 -0
  77. package/src/common/input/WriteBuffer.ts +69 -32
  78. package/src/common/input/XParseColor.ts +2 -2
  79. package/src/common/parser/ApcParser.ts +196 -0
  80. package/src/common/parser/Constants.ts +14 -4
  81. package/src/common/parser/DcsParser.ts +11 -12
  82. package/src/common/parser/EscapeSequenceParser.ts +205 -63
  83. package/src/common/parser/OscParser.ts +11 -12
  84. package/src/common/parser/Params.ts +27 -8
  85. package/src/common/parser/Types.ts +36 -2
  86. package/src/common/public/BufferLineApiView.ts +2 -2
  87. package/src/common/public/BufferNamespaceApi.ts +3 -3
  88. package/src/common/public/ParserApi.ts +3 -0
  89. package/src/common/services/BufferService.ts +14 -9
  90. package/src/common/services/CharsetService.ts +4 -0
  91. package/src/common/services/CoreService.ts +22 -9
  92. package/src/common/services/DecorationService.ts +258 -8
  93. package/src/common/services/LogService.ts +1 -31
  94. package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +21 -132
  95. package/src/common/services/OptionsService.ts +13 -4
  96. package/src/common/services/ServiceRegistry.ts +9 -7
  97. package/src/common/services/Services.ts +49 -40
  98. package/src/common/services/UnicodeService.ts +1 -1
  99. package/typings/xterm.d.ts +318 -34
  100. package/src/common/Clone.ts +0 -23
  101. package/src/common/TypedArrayUtils.ts +0 -17
  102. package/src/vs/base/browser/browser.ts +0 -141
  103. package/src/vs/base/browser/canIUse.ts +0 -49
  104. package/src/vs/base/browser/dom.ts +0 -2369
  105. package/src/vs/base/browser/fastDomNode.ts +0 -316
  106. package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
  107. package/src/vs/base/browser/iframe.ts +0 -135
  108. package/src/vs/base/browser/keyboardEvent.ts +0 -213
  109. package/src/vs/base/browser/mouseEvent.ts +0 -229
  110. package/src/vs/base/browser/touch.ts +0 -372
  111. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
  112. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
  113. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
  114. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
  115. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
  116. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
  117. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
  118. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
  119. package/src/vs/base/browser/ui/widget.ts +0 -57
  120. package/src/vs/base/browser/window.ts +0 -14
  121. package/src/vs/base/common/arrays.ts +0 -887
  122. package/src/vs/base/common/arraysFind.ts +0 -202
  123. package/src/vs/base/common/assert.ts +0 -71
  124. package/src/vs/base/common/async.ts +0 -1992
  125. package/src/vs/base/common/cancellation.ts +0 -148
  126. package/src/vs/base/common/charCode.ts +0 -450
  127. package/src/vs/base/common/collections.ts +0 -140
  128. package/src/vs/base/common/decorators.ts +0 -130
  129. package/src/vs/base/common/equals.ts +0 -146
  130. package/src/vs/base/common/errors.ts +0 -303
  131. package/src/vs/base/common/event.ts +0 -1778
  132. package/src/vs/base/common/functional.ts +0 -32
  133. package/src/vs/base/common/hash.ts +0 -316
  134. package/src/vs/base/common/iterator.ts +0 -159
  135. package/src/vs/base/common/keyCodes.ts +0 -526
  136. package/src/vs/base/common/keybindings.ts +0 -284
  137. package/src/vs/base/common/lazy.ts +0 -47
  138. package/src/vs/base/common/lifecycle.ts +0 -801
  139. package/src/vs/base/common/linkedList.ts +0 -142
  140. package/src/vs/base/common/map.ts +0 -202
  141. package/src/vs/base/common/numbers.ts +0 -98
  142. package/src/vs/base/common/observable.ts +0 -76
  143. package/src/vs/base/common/observableInternal/api.ts +0 -31
  144. package/src/vs/base/common/observableInternal/autorun.ts +0 -281
  145. package/src/vs/base/common/observableInternal/base.ts +0 -489
  146. package/src/vs/base/common/observableInternal/debugName.ts +0 -145
  147. package/src/vs/base/common/observableInternal/derived.ts +0 -428
  148. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
  149. package/src/vs/base/common/observableInternal/logging.ts +0 -328
  150. package/src/vs/base/common/observableInternal/promise.ts +0 -209
  151. package/src/vs/base/common/observableInternal/utils.ts +0 -610
  152. package/src/vs/base/common/platform.ts +0 -281
  153. package/src/vs/base/common/scrollable.ts +0 -522
  154. package/src/vs/base/common/sequence.ts +0 -34
  155. package/src/vs/base/common/stopwatch.ts +0 -43
  156. package/src/vs/base/common/strings.ts +0 -557
  157. package/src/vs/base/common/symbols.ts +0 -9
  158. package/src/vs/base/common/uint.ts +0 -59
  159. package/src/vs/patches/nls.ts +0 -90
  160. package/src/vs/typings/base-common.d.ts +0 -20
  161. package/src/vs/typings/require.d.ts +0 -42
  162. package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
  163. package/src/vs/typings/vscode-globals-product.d.ts +0 -33
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Copyright (c) 2026 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ *
5
+ * Minimal DOM helpers for xterm.js browser code.
6
+ */
7
+
8
+ import { IntervalTimer } from 'common/Async';
9
+ import { IDisposable } from 'common/Lifecycle';
10
+
11
+ export function getWindow(e: Node | UIEvent | undefined | null): Window {
12
+ const candidateNode = e as Node | undefined | null;
13
+ if (candidateNode?.ownerDocument?.defaultView) {
14
+ return candidateNode.ownerDocument.defaultView;
15
+ }
16
+
17
+ const candidateEvent = e as UIEvent | undefined | null;
18
+ if (candidateEvent?.view) {
19
+ return candidateEvent.view;
20
+ }
21
+
22
+ return window;
23
+ }
24
+
25
+ class DomListener implements IDisposable {
26
+ private _handler: ((e: any) => void) | null;
27
+ private _node: EventTarget | null;
28
+ private readonly _type: string;
29
+ private readonly _options: boolean | AddEventListenerOptions | undefined;
30
+
31
+ constructor(node: EventTarget, type: string, handler: (e: any) => void, options?: boolean | AddEventListenerOptions) {
32
+ this._node = node;
33
+ this._type = type;
34
+ this._handler = handler;
35
+ this._options = options;
36
+ node.addEventListener(type, handler, options);
37
+ }
38
+
39
+ public dispose(): void {
40
+ if (!this._node || !this._handler) {
41
+ return;
42
+ }
43
+ this._node.removeEventListener(this._type, this._handler, this._options);
44
+ this._node = null;
45
+ this._handler = null;
46
+ }
47
+ }
48
+
49
+ export function addDisposableListener<K extends keyof GlobalEventHandlersEventMap>(node: EventTarget, type: K, handler: (event: GlobalEventHandlersEventMap[K]) => void, useCapture?: boolean): IDisposable;
50
+ export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable;
51
+ export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, options: AddEventListenerOptions): IDisposable;
52
+ export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCaptureOrOptions?: boolean | AddEventListenerOptions): IDisposable {
53
+ return new DomListener(node, type, handler, useCaptureOrOptions);
54
+ }
55
+
56
+ export function addStandardDisposableListener(node: HTMLElement, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable {
57
+ return addDisposableListener(node, type, handler, useCapture);
58
+ }
59
+
60
+ export const eventType = {
61
+ CLICK: 'click',
62
+ MOUSE_DOWN: 'mousedown',
63
+ MOUSE_OVER: 'mouseover',
64
+ MOUSE_LEAVE: 'mouseleave',
65
+ KEY_DOWN: 'keydown',
66
+ KEY_UP: 'keyup',
67
+ INPUT: 'input',
68
+ BLUR: 'blur',
69
+ FOCUS: 'focus',
70
+ CHANGE: 'change',
71
+ POINTER_DOWN: 'pointerdown',
72
+ POINTER_MOVE: 'pointermove',
73
+ POINTER_UP: 'pointerup',
74
+ MOUSE_WHEEL: 'wheel',
75
+ WHEEL: 'wheel'
76
+ } as const;
77
+
78
+ export function getDomNodePagePosition(domNode: HTMLElement): { left: number, top: number, width: number, height: number } {
79
+ const bb = domNode.getBoundingClientRect();
80
+ const win = getWindow(domNode);
81
+ return {
82
+ left: bb.left + win.scrollX,
83
+ top: bb.top + win.scrollY,
84
+ width: bb.width,
85
+ height: bb.height
86
+ };
87
+ }
88
+
89
+ class AnimationFrameQueueItem implements IDisposable {
90
+ private _canceled = false;
91
+
92
+ constructor(private readonly _runner: () => void, public priority: number) {
93
+ }
94
+
95
+ public dispose(): void {
96
+ this._canceled = true;
97
+ }
98
+
99
+ public execute(): void {
100
+ if (this._canceled) {
101
+ return;
102
+ }
103
+ try {
104
+ this._runner();
105
+ } catch (e) {
106
+ console.error(e);
107
+ }
108
+ }
109
+
110
+ public static sort(a: AnimationFrameQueueItem, b: AnimationFrameQueueItem): number {
111
+ return b.priority - a.priority;
112
+ }
113
+ }
114
+
115
+ interface IWindowAnimationFrameState {
116
+ next: AnimationFrameQueueItem[];
117
+ current: AnimationFrameQueueItem[];
118
+ animFrameRequested: boolean;
119
+ inAnimationFrameRunner: boolean;
120
+ }
121
+
122
+ const animationFrameState = new Map<Window, IWindowAnimationFrameState>();
123
+
124
+ function getAnimationFrameState(targetWindow: Window): IWindowAnimationFrameState {
125
+ let state = animationFrameState.get(targetWindow);
126
+ if (!state) {
127
+ state = {
128
+ next: [],
129
+ current: [],
130
+ animFrameRequested: false,
131
+ inAnimationFrameRunner: false
132
+ };
133
+ animationFrameState.set(targetWindow, state);
134
+ }
135
+ return state;
136
+ }
137
+
138
+ function animationFrameRunner(targetWindow: Window): void {
139
+ const state = getAnimationFrameState(targetWindow);
140
+ state.animFrameRequested = false;
141
+
142
+ state.current = state.next;
143
+ state.next = [];
144
+
145
+ state.inAnimationFrameRunner = true;
146
+ while (state.current.length > 0) {
147
+ state.current.sort(AnimationFrameQueueItem.sort);
148
+ const top = state.current.shift()!;
149
+ top.execute();
150
+ }
151
+ state.inAnimationFrameRunner = false;
152
+ }
153
+
154
+ export function scheduleAtNextAnimationFrame(targetWindow: Window, runner: () => void, priority: number = 0): IDisposable {
155
+ const state = getAnimationFrameState(targetWindow);
156
+ const item = new AnimationFrameQueueItem(runner, priority);
157
+ state.next.push(item);
158
+
159
+ if (!state.animFrameRequested) {
160
+ state.animFrameRequested = true;
161
+ targetWindow.requestAnimationFrame(() => animationFrameRunner(targetWindow));
162
+ }
163
+
164
+ return item;
165
+ }
166
+
167
+ export class WindowIntervalTimer extends IntervalTimer {
168
+ private readonly _defaultTarget?: Window;
169
+
170
+ constructor(node?: Node) {
171
+ super();
172
+ this._defaultTarget = node ? getWindow(node) : undefined;
173
+ }
174
+
175
+ public cancelAndSet(runner: () => void, interval: number, targetWindow?: Window): void {
176
+ super.cancelAndSet(runner, interval, targetWindow ?? this._defaultTarget ?? window);
177
+ }
178
+ }
@@ -4,12 +4,12 @@
4
4
  */
5
5
 
6
6
  import { IBufferCellPosition, ILink, ILinkDecorations, ILinkWithState, ILinkifier2, ILinkifierEvent } from 'browser/Types';
7
- import { Disposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
7
+ import { Disposable, dispose, toDisposable } from 'common/Lifecycle';
8
8
  import { IDisposable } from 'common/Types';
9
9
  import { IBufferService } from 'common/services/Services';
10
- import { ILinkProviderService, IMouseService, IRenderService } from './services/Services';
11
- import { Emitter } from 'vs/base/common/event';
12
- import { addDisposableListener } from 'vs/base/browser/dom';
10
+ import { ILinkProviderService, IMouseCoordsService, IRenderService } from './services/Services';
11
+ import { Emitter } from 'common/Event';
12
+ import { addDisposableListener } from 'browser/Dom';
13
13
 
14
14
  export class Linkifier extends Disposable implements ILinkifier2 {
15
15
  public get currentLink(): ILinkWithState | undefined { return this._currentLink; }
@@ -30,7 +30,7 @@ export class Linkifier extends Disposable implements ILinkifier2 {
30
30
 
31
31
  constructor(
32
32
  private readonly _element: HTMLElement,
33
- @IMouseService private readonly _mouseService: IMouseService,
33
+ @IMouseCoordsService private readonly _mouseCoordsService: IMouseCoordsService,
34
34
  @IRenderService private readonly _renderService: IRenderService,
35
35
  @IBufferService private readonly _bufferService: IBufferService,
36
36
  @ILinkProviderService private readonly _linkProviderService: ILinkProviderService
@@ -60,7 +60,7 @@ export class Linkifier extends Disposable implements ILinkifier2 {
60
60
  private _handleMouseMove(event: MouseEvent): void {
61
61
  this._lastMouseEvent = event;
62
62
 
63
- const position = this._positionFromMouseEvent(event, this._element, this._mouseService);
63
+ const position = this._positionFromMouseEvent(event, this._element);
64
64
  if (!position) {
65
65
  return;
66
66
  }
@@ -222,7 +222,7 @@ export class Linkifier extends Disposable implements ILinkifier2 {
222
222
  return;
223
223
  }
224
224
 
225
- const position = this._positionFromMouseEvent(event, this._element, this._mouseService);
225
+ const position = this._positionFromMouseEvent(event, this._element);
226
226
  if (!position) {
227
227
  return;
228
228
  }
@@ -251,7 +251,7 @@ export class Linkifier extends Disposable implements ILinkifier2 {
251
251
  return;
252
252
  }
253
253
 
254
- const position = this._positionFromMouseEvent(this._lastMouseEvent, this._element, this._mouseService);
254
+ const position = this._positionFromMouseEvent(this._lastMouseEvent, this._element);
255
255
 
256
256
  if (!position) {
257
257
  return;
@@ -312,7 +312,7 @@ export class Linkifier extends Disposable implements ILinkifier2 {
312
312
  this._clearCurrentLink(start, end);
313
313
  if (this._lastMouseEvent) {
314
314
  // re-eval previously active link after changes
315
- const position = this._positionFromMouseEvent(this._lastMouseEvent, this._element, this._mouseService!);
315
+ const position = this._positionFromMouseEvent(this._lastMouseEvent, this._element);
316
316
  if (position) {
317
317
  this._askForLink(position, false);
318
318
  }
@@ -378,8 +378,8 @@ export class Linkifier extends Disposable implements ILinkifier2 {
378
378
  * Get the buffer position from a mouse event
379
379
  * @param event
380
380
  */
381
- private _positionFromMouseEvent(event: MouseEvent, element: HTMLElement, mouseService: IMouseService): IBufferCellPosition | undefined {
382
- const coords = mouseService.getCoords(event, element, this._bufferService.cols, this._bufferService.rows);
381
+ private _positionFromMouseEvent(event: MouseEvent, element: HTMLElement): IBufferCellPosition | undefined {
382
+ const coords = this._mouseCoordsService.getCoords(event, element, this._bufferService.cols, this._bufferService.rows);
383
383
  if (!coords) {
384
384
  return;
385
385
  }
@@ -6,9 +6,12 @@
6
6
  import { IBufferRange, ILink } from 'browser/Types';
7
7
  import { ILinkProvider } from 'browser/services/Services';
8
8
  import { CellData } from 'common/buffer/CellData';
9
+ import { IBufferLine } from 'common/Types';
9
10
  import { IBufferService, IOptionsService, IOscLinkService } from 'common/services/Services';
10
11
 
11
12
  export class OscLinkProvider implements ILinkProvider {
13
+ private readonly _workCell = new CellData();
14
+
12
15
  constructor(
13
16
  @IBufferService private readonly _bufferService: IBufferService,
14
17
  @IOptionsService private readonly _optionsService: IOptionsService,
@@ -25,7 +28,7 @@ export class OscLinkProvider implements ILinkProvider {
25
28
 
26
29
  const result: ILink[] = [];
27
30
  const linkHandler = this._optionsService.rawOptions.linkHandler;
28
- const cell = new CellData();
31
+ const cell = this._workCell;
29
32
  const lineLength = line.getTrimmedLength();
30
33
  let currentLinkId = -1;
31
34
  let currentStart = -1;
@@ -55,19 +58,8 @@ export class OscLinkProvider implements ILinkProvider {
55
58
  if (finishLink || (currentStart !== -1 && x === lineLength - 1)) {
56
59
  const text = this._oscLinkService.getLinkData(currentLinkId)?.uri;
57
60
  if (text) {
58
- // These ranges are 1-based
59
- const range: IBufferRange = {
60
- start: {
61
- x: currentStart + 1,
62
- y
63
- },
64
- end: {
65
- // Offset end x if it's a link that ends on the last cell in the line
66
- x: x + (!finishLink && x === lineLength - 1 ? 1 : 0),
67
- y
68
- }
69
- };
70
-
61
+ const endX = x + (!finishLink && x === lineLength - 1 ? 1 : 0);
62
+ const range = this._getRangeWithLineWrap(y, currentStart, endX, currentLinkId);
71
63
  let ignoreLink = false;
72
64
  if (!linkHandler?.allowNonHttpProtocols) {
73
65
  try {
@@ -109,6 +101,82 @@ export class OscLinkProvider implements ILinkProvider {
109
101
  // id
110
102
  callback(result);
111
103
  }
104
+
105
+ /**
106
+ * Expand a single-line OSC 8 range to a contiguous wrapped range for the same link id.
107
+ */
108
+ private _getRangeWithLineWrap(y: number, startX: number, endX: number, linkId: number): IBufferRange {
109
+ let startY = y;
110
+ let finalStartX = startX;
111
+ let endY = y;
112
+ let finalEndX = endX;
113
+
114
+ // Expand upward only when this segment starts at column 0 and the current line is wrapped.
115
+ while (finalStartX === 0) {
116
+ const currentLine = this._bufferService.buffer.lines.get(startY - 1);
117
+ if (!currentLine?.isWrapped) {
118
+ break;
119
+ }
120
+ const previousLine = this._bufferService.buffer.lines.get(startY - 2);
121
+ if (!previousLine) {
122
+ break;
123
+ }
124
+ const previousLineLength = previousLine.getTrimmedLength();
125
+ if (previousLineLength === 0 || !this._hasUrlId(previousLine, previousLineLength - 1, linkId)) {
126
+ break;
127
+ }
128
+ let previousStartX = previousLineLength - 1;
129
+ while (previousStartX > 0 && this._hasUrlId(previousLine, previousStartX - 1, linkId)) {
130
+ previousStartX--;
131
+ }
132
+ startY--;
133
+ finalStartX = previousStartX;
134
+ }
135
+
136
+ // Expand downward only when this segment reaches trimmed EOL and the next line is wrapped.
137
+ while (true) {
138
+ const currentLine = this._bufferService.buffer.lines.get(endY - 1);
139
+ if (!currentLine) {
140
+ break;
141
+ }
142
+ const currentLineLength = currentLine.getTrimmedLength();
143
+ if (finalEndX !== currentLineLength) {
144
+ break;
145
+ }
146
+ const nextLine = this._bufferService.buffer.lines.get(endY);
147
+ if (!nextLine?.isWrapped) {
148
+ break;
149
+ }
150
+ const nextLineLength = nextLine.getTrimmedLength();
151
+ if (nextLineLength === 0 || !this._hasUrlId(nextLine, 0, linkId)) {
152
+ break;
153
+ }
154
+ let nextEndX = 1;
155
+ while (nextEndX < nextLineLength && this._hasUrlId(nextLine, nextEndX, linkId)) {
156
+ nextEndX++;
157
+ }
158
+ endY++;
159
+ finalEndX = nextEndX;
160
+ }
161
+
162
+ // IBufferRange uses 1-based coordinates.
163
+ return {
164
+ start: {
165
+ x: finalStartX + 1,
166
+ y: startY
167
+ },
168
+ end: {
169
+ x: finalEndX,
170
+ y: endY
171
+ }
172
+ };
173
+ }
174
+
175
+ private _hasUrlId(line: IBufferLine, x: number, linkId: number): boolean {
176
+ const cell = this._workCell;
177
+ line.loadCell(x, cell);
178
+ return !!cell.hasExtendedAttrs() && cell.extended.urlId === linkId;
179
+ }
112
180
  }
113
181
 
114
182
  function defaultActivate(e: MouseEvent, uri: string): void {
@@ -40,8 +40,8 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback {
40
40
  public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void {
41
41
  this._rowCount = rowCount;
42
42
  // Get the min/max row start/end for the arg values
43
- rowStart = rowStart !== undefined ? rowStart : 0;
44
- rowEnd = rowEnd !== undefined ? rowEnd : this._rowCount - 1;
43
+ rowStart = rowStart ?? 0;
44
+ rowEnd = rowEnd ?? this._rowCount - 1;
45
45
  // Set the properties to the updated values
46
46
  this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart;
47
47
  this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd;
@@ -37,8 +37,8 @@ export class TimeBasedDebouncer implements IRenderDebouncer {
37
37
  public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void {
38
38
  this._rowCount = rowCount;
39
39
  // Get the min/max row start/end for the arg values
40
- rowStart = rowStart !== undefined ? rowStart : 0;
41
- rowEnd = rowEnd !== undefined ? rowEnd : this._rowCount - 1;
40
+ rowStart = rowStart ?? 0;
41
+ rowEnd = rowEnd ?? this._rowCount - 1;
42
42
  // Set the properties to the updated values
43
43
  this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart;
44
44
  this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd;
@@ -5,9 +5,9 @@
5
5
 
6
6
  import { CharData, IColor, ICoreTerminal, ITerminalOptions } from 'common/Types';
7
7
  import { IBuffer } from 'common/buffer/Types';
8
- import { IDisposable, Terminal as ITerminalApi } from '@xterm/xterm';
8
+ import { IDisposable, IRenderDimensions as IRenderDimensionsApi, Terminal as ITerminalApi } from '@xterm/xterm';
9
9
  import { channels, css } from 'common/Color';
10
- import type { Event } from 'vs/base/common/event';
10
+ import type { IEvent } from 'common/Event';
11
11
 
12
12
  /**
13
13
  * A portion of the public API that are implemented identially internally and simply passed through.
@@ -21,13 +21,14 @@ export interface ITerminal extends InternalPassthroughApis, ICoreTerminal {
21
21
  linkifier: ILinkifier2 | undefined;
22
22
  options: Required<ITerminalOptions>;
23
23
 
24
- onBlur: Event<void>;
25
- onFocus: Event<void>;
26
- onA11yChar: Event<string>;
27
- onA11yTab: Event<number>;
28
- onWillOpen: Event<HTMLElement>;
24
+ readonly dimensions: IRenderDimensionsApi | undefined;
29
25
 
30
- cancel(ev: MouseEvent | WheelEvent | KeyboardEvent | InputEvent, force?: boolean): boolean | void;
26
+ onBlur: IEvent<void>;
27
+ onFocus: IEvent<void>;
28
+ onDimensionsChange: IEvent<IRenderDimensionsApi>;
29
+ onA11yChar: IEvent<string>;
30
+ onA11yTab: IEvent<number>;
31
+ onWillOpen: IEvent<HTMLElement>;
31
32
  }
32
33
 
33
34
  export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
@@ -98,7 +99,7 @@ export interface IPartialColorSet {
98
99
 
99
100
  export interface IViewport extends IDisposable {
100
101
  scrollBarWidth: number;
101
- readonly onRequestScrollLines: Event<{ amount: number, suppressScrollEvent: boolean }>;
102
+ readonly onRequestScrollLines: IEvent<{ amount: number, suppressScrollEvent: boolean }>;
102
103
  syncScrollArea(immediate?: boolean, force?: boolean): void;
103
104
  getLinesScrolled(ev: WheelEvent): number;
104
105
  getBufferElements(startLine: number, endLine?: number): { bufferElements: HTMLElement[], cursorElement?: HTMLElement };
@@ -128,8 +129,8 @@ export interface ILinkWithState {
128
129
  }
129
130
 
130
131
  export interface ILinkifier2 extends IDisposable {
131
- onShowLinkUnderline: Event<ILinkifierEvent>;
132
- onHideLinkUnderline: Event<ILinkifierEvent>;
132
+ onShowLinkUnderline: IEvent<ILinkifierEvent>;
133
+ onHideLinkUnderline: IEvent<ILinkifierEvent>;
133
134
  readonly currentLink: ILinkWithState | undefined;
134
135
  }
135
136
 
@@ -5,14 +5,14 @@
5
5
 
6
6
  import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
7
7
  import { ViewportConstants } from 'browser/shared/Constants';
8
- import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
9
- import { IBufferService, ICoreMouseService, IOptionsService } from 'common/services/Services';
8
+ import { Disposable, toDisposable } from 'common/Lifecycle';
9
+ import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from 'common/services/Services';
10
10
  import { CoreMouseEventType } from 'common/Types';
11
- import { scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
12
- import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
13
- import type { ScrollableElementChangeOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
14
- import { Emitter, Event } from 'vs/base/common/event';
15
- import { Scrollable, ScrollbarVisibility, type ScrollEvent } from 'vs/base/common/scrollable';
11
+ import { scheduleAtNextAnimationFrame } from 'browser/Dom';
12
+ import { SmoothScrollableElement } from 'browser/scrollable/scrollableElement';
13
+ import type { IScrollableElementChangeOptions } from 'browser/scrollable/scrollableElementOptions';
14
+ import { Emitter, EventUtils } from 'common/Event';
15
+ import { Scrollable, ScrollbarVisibility, type IScrollEvent } from 'browser/scrollable/scrollable';
16
16
 
17
17
  export class Viewport extends Disposable {
18
18
 
@@ -27,13 +27,15 @@ export class Viewport extends Disposable {
27
27
  private _isSyncing: boolean = false;
28
28
  private _isHandlingScroll: boolean = false;
29
29
  private _suppressOnScrollHandler: boolean = false;
30
+ private _needsSyncOnRender: boolean = false;
30
31
 
31
32
  constructor(
32
33
  element: HTMLElement,
33
34
  screenElement: HTMLElement,
34
35
  @IBufferService private readonly _bufferService: IBufferService,
35
36
  @ICoreBrowserService coreBrowserService: ICoreBrowserService,
36
- @ICoreMouseService coreMouseService: ICoreMouseService,
37
+ @ICoreService private readonly _coreService: ICoreService,
38
+ @IMouseStateService mouseStateService: IMouseStateService,
37
39
  @IThemeService themeService: IThemeService,
38
40
  @IOptionsService private readonly _optionsService: IOptionsService,
39
41
  @IRenderService private readonly _renderService: IRenderService
@@ -51,26 +53,28 @@ export class Viewport extends Disposable {
51
53
  }));
52
54
 
53
55
  this._scrollableElement = this._register(new SmoothScrollableElement(screenElement, {
54
- vertical: ScrollbarVisibility.Auto,
55
- horizontal: ScrollbarVisibility.Hidden,
56
+ vertical: ScrollbarVisibility.AUTO,
57
+ horizontal: ScrollbarVisibility.HIDDEN,
56
58
  useShadows: false,
57
59
  mouseWheelSmoothScroll: true,
60
+ verticalHasArrows: this._optionsService.rawOptions.scrollbar?.showArrows ?? false,
58
61
  ...this._getChangeOptions()
59
62
  }, scrollable));
60
63
  this._register(this._optionsService.onMultipleOptionChange([
61
64
  'scrollSensitivity',
62
65
  'fastScrollSensitivity',
63
- 'overviewRuler'
66
+ 'scrollbar'
64
67
  ], () => this._scrollableElement.updateOptions(this._getChangeOptions())));
65
68
  // Don't handle mouse wheel if wheel events are supported by the current mouse prototcol
66
- this._register(coreMouseService.onProtocolChange(type => {
69
+ this._register(mouseStateService.onProtocolChange(type => {
67
70
  this._scrollableElement.updateOptions({
68
71
  handleMouseWheel: !(type & CoreMouseEventType.WHEEL)
69
72
  });
70
73
  }));
71
74
 
72
75
  this._scrollableElement.setScrollDimensions({ height: 0, scrollHeight: 0 });
73
- this._register(Event.runAndSubscribe(themeService.onChangeColors, () => {
76
+ this._register(EventUtils.runAndSubscribe(themeService.onChangeColors, () => {
77
+ element.style.backgroundColor = themeService.colors.background.css;
74
78
  this._scrollableElement.getDomNode().style.backgroundColor = themeService.colors.background.css;
75
79
  }));
76
80
  element.appendChild(this._scrollableElement.getDomNode());
@@ -79,15 +83,15 @@ export class Viewport extends Disposable {
79
83
  this._styleElement = coreBrowserService.mainDocument.createElement('style');
80
84
  screenElement.appendChild(this._styleElement);
81
85
  this._register(toDisposable(() => this._styleElement.remove()));
82
- this._register(Event.runAndSubscribe(themeService.onChangeColors, () => {
86
+ this._register(EventUtils.runAndSubscribe(themeService.onChangeColors, () => {
83
87
  this._styleElement.textContent = [
84
- `.xterm .xterm-scrollable-element > .scrollbar > .slider {`,
88
+ `.xterm .xterm-scrollable-element > .xterm-scrollbar > .xterm-slider {`,
85
89
  ` background: ${themeService.colors.scrollbarSliderBackground.css};`,
86
90
  `}`,
87
- `.xterm .xterm-scrollable-element > .scrollbar > .slider:hover {`,
91
+ `.xterm .xterm-scrollable-element > .xterm-scrollbar > .xterm-slider:hover {`,
88
92
  ` background: ${themeService.colors.scrollbarSliderHoverBackground.css};`,
89
93
  `}`,
90
- `.xterm .xterm-scrollable-element > .scrollbar > .slider.active {`,
94
+ `.xterm .xterm-scrollable-element > .xterm-scrollbar > .xterm-slider.xterm-active {`,
91
95
  ` background: ${themeService.colors.scrollbarSliderActiveBackground.css};`,
92
96
  `}`
93
97
  ].join('\n');
@@ -102,7 +106,18 @@ export class Viewport extends Disposable {
102
106
  }));
103
107
  this._register(this._bufferService.onScroll(() => this._sync()));
104
108
 
109
+ // Flush deferred viewport sync after a render completes (e.g. after ESU ends
110
+ // synchronized output mode). This ensures DOM scroll position updates atomically
111
+ // with the canvas render.
112
+ this._register(this._renderService.onRender(() => {
113
+ if (this._needsSyncOnRender) {
114
+ this._needsSyncOnRender = false;
115
+ this._sync();
116
+ }
117
+ }));
118
+
105
119
  this._register(this._scrollableElement.onScroll(e => this._handleScroll(e)));
120
+
106
121
  }
107
122
 
108
123
  public scrollLines(disp: number): void {
@@ -123,11 +138,18 @@ export class Viewport extends Disposable {
123
138
  });
124
139
  }
125
140
 
126
- private _getChangeOptions(): ScrollableElementChangeOptions {
141
+ private _getChangeOptions(): IScrollableElementChangeOptions {
142
+ const showScrollbar = this._optionsService.rawOptions.scrollbar?.showScrollbar ?? true;
143
+ const showArrows = this._optionsService.rawOptions.scrollbar?.showArrows ?? false;
144
+ const verticalScrollbarSize = showScrollbar
145
+ ? (this._optionsService.rawOptions.scrollbar?.width ?? ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH)
146
+ : 0;
127
147
  return {
128
148
  mouseWheelScrollSensitivity: this._optionsService.rawOptions.scrollSensitivity,
129
149
  fastScrollSensitivity: this._optionsService.rawOptions.fastScrollSensitivity,
130
- verticalScrollbarSize: this._optionsService.rawOptions.overviewRuler?.width || ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH
150
+ vertical: showScrollbar ? ScrollbarVisibility.AUTO : ScrollbarVisibility.HIDDEN,
151
+ verticalScrollbarSize,
152
+ verticalHasArrows: showArrows
131
153
  };
132
154
  }
133
155
 
@@ -151,6 +173,12 @@ export class Viewport extends Disposable {
151
173
  if (!this._renderService || this._isSyncing) {
152
174
  return;
153
175
  }
176
+ // Defer DOM scroll updates during synchronized output to prevent visible
177
+ // scroll position flickering while the canvas content is frozen.
178
+ if (this._coreService.decPrivateModes.synchronizedOutput) {
179
+ this._needsSyncOnRender = true;
180
+ return;
181
+ }
154
182
  this._isSyncing = true;
155
183
 
156
184
  // Ignore any onScroll event that happens as a result of dimensions changing as this should
@@ -173,7 +201,7 @@ export class Viewport extends Disposable {
173
201
  this._isSyncing = false;
174
202
  }
175
203
 
176
- private _handleScroll(e: ScrollEvent): void {
204
+ private _handleScroll(e: IScrollEvent): void {
177
205
  if (!this._renderService) {
178
206
  return;
179
207
  }
@@ -189,4 +217,11 @@ export class Viewport extends Disposable {
189
217
  }
190
218
  this._isHandlingScroll = false;
191
219
  }
220
+
221
+ public handleTouchScroll(translationY: number): void {
222
+ const pos = this._scrollableElement.getScrollPosition();
223
+ this._scrollableElement.setScrollPosition({
224
+ scrollTop: pos.scrollTop - translationY
225
+ });
226
+ }
192
227
  }
@@ -4,7 +4,7 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
 
6
6
  import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
7
- import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
7
+ import { Disposable, toDisposable } from 'common/Lifecycle';
8
8
  import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services';
9
9
 
10
10
  export class BufferDecorationRenderer extends Disposable {