@xterm/xterm 6.1.0-beta.2 → 6.1.0-beta.200

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 +60 -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 +36 -24
  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 +4 -2
  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 -16
  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 +29 -21
  56. package/src/common/Event.ts +118 -0
  57. package/src/common/InputHandler.ts +256 -36
  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 +36 -16
  63. package/src/common/Version.ts +9 -0
  64. package/src/common/buffer/Buffer.ts +22 -16
  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/Charsets.ts +1 -1
  71. package/src/common/data/EscapeSequences.ts +71 -70
  72. package/src/common/input/Keyboard.ts +14 -7
  73. package/src/common/input/KittyKeyboard.ts +519 -0
  74. package/src/common/input/Win32InputMode.ts +297 -0
  75. package/src/common/input/WriteBuffer.ts +34 -2
  76. package/src/common/input/XParseColor.ts +2 -2
  77. package/src/common/parser/ApcParser.ts +245 -0
  78. package/src/common/parser/Constants.ts +22 -4
  79. package/src/common/parser/DcsParser.ts +5 -5
  80. package/src/common/parser/EscapeSequenceParser.ts +155 -43
  81. package/src/common/parser/OscParser.ts +5 -5
  82. package/src/common/parser/Types.ts +34 -1
  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 -7
  93. package/src/common/services/Services.ts +47 -44
  94. package/src/common/services/UnicodeService.ts +1 -1
  95. package/typings/xterm.d.ts +320 -45
  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
@@ -24,6 +24,7 @@ export const enum RowCss {
24
24
  UNDERLINE_CLASS = 'xterm-underline',
25
25
  OVERLINE_CLASS = 'xterm-overline',
26
26
  STRIKETHROUGH_CLASS = 'xterm-strikethrough',
27
+ BLINK_HIDDEN_CLASS = 'xterm-blink-hidden',
27
28
  CURSOR_CLASS = 'xterm-cursor',
28
29
  CURSOR_BLINK_CLASS = 'xterm-cursor-blink',
29
30
  CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block',
@@ -66,13 +67,18 @@ export class DomRendererRowFactory {
66
67
  cursorInactiveStyle: string | undefined,
67
68
  cursorX: number,
68
69
  cursorBlink: boolean,
70
+ blinkOn: boolean,
69
71
  cellWidth: number,
70
72
  widthCache: WidthCache,
71
73
  linkStart: number,
72
- linkEnd: number
74
+ linkEnd: number,
75
+ rowInfo?: { hasBlinkingCells: boolean }
73
76
  ): HTMLSpanElement[] {
74
77
 
75
78
  const elements: HTMLSpanElement[] = [];
79
+ if (rowInfo) {
80
+ rowInfo.hasBlinkingCells = false;
81
+ }
76
82
  const joinedRanges = this._characterJoinerService.getJoinedCharacters(row);
77
83
  const colors = this._themeService.colors;
78
84
 
@@ -118,7 +124,7 @@ export class DomRendererRowFactory {
118
124
  // Process any joined character ranges as needed. Because of how the
119
125
  // ranges are produced, we know that they are valid for the characters
120
126
  // and attributes of our input.
121
- let cell = this._workCell;
127
+ let cell: ICellData = this._workCell;
122
128
  if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) {
123
129
  const range = joinedRanges.shift()!;
124
130
  // If the ligature's selection state is not consistent, don't join it. This helps the
@@ -153,6 +159,13 @@ export class DomRendererRowFactory {
153
159
  const isInSelection = this._isCellInSelection(x, row);
154
160
  const isCursorCell = isCursorRow && x === cursorX;
155
161
  const isLinkHover = hasHover && x >= linkStart && x <= linkEnd;
162
+ if (rowInfo && cell.isBlink()) {
163
+ rowInfo.hasBlinkingCells = true;
164
+ }
165
+ const isBlinkHidden = !blinkOn && cell.isBlink();
166
+ if (isBlinkHidden) {
167
+ classes.push(RowCss.BLINK_HIDDEN_CLASS);
168
+ }
156
169
 
157
170
  let isDecorated = false;
158
171
  this._decorationService.forEachDecorationAtCell(x, row, undefined, d => {
@@ -397,7 +410,7 @@ export class DomRendererRowFactory {
397
410
  break;
398
411
  case Attributes.CM_RGB:
399
412
  resolvedBg = channels.toColor(bg >> 16, bg >> 8 & 0xFF, bg & 0xFF);
400
- this._addStyle(charElement, `background-color:#${padStart((bg >>> 0).toString(16), '0', 6)}`);
413
+ this._addStyle(charElement, `background-color:#${(bg >>> 0).toString(16).padStart(6, '0')}`);
401
414
  break;
402
415
  case Attributes.CM_DEFAULT:
403
416
  default:
@@ -434,7 +447,7 @@ export class DomRendererRowFactory {
434
447
  (fg ) & 0xFF
435
448
  );
436
449
  if (!this._applyMinimumContrast(charElement, resolvedBg, color, cell, bgOverride, fgOverride)) {
437
- this._addStyle(charElement, `color:#${padStart(fg.toString(16), '0', 6)}`);
450
+ this._addStyle(charElement, `color:#${fg.toString(16).padStart(6, '0')}`);
438
451
  }
439
452
  break;
440
453
  case Attributes.CM_DEFAULT:
@@ -494,8 +507,8 @@ export class DomRendererRowFactory {
494
507
  // Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from
495
508
  // non-dim cells
496
509
  const ratio = this._optionsService.rawOptions.minimumContrastRatio / (cell.isDim() ? 2 : 1);
497
- adjustedColor = color.ensureContrastRatio(bgOverride || bg, fgOverride || fg, ratio);
498
- cache.setColor((bgOverride || bg).rgba, (fgOverride || fg).rgba, adjustedColor ?? null);
510
+ adjustedColor = color.ensureContrastRatio(bgOverride ?? bg, fgOverride ?? fg, ratio);
511
+ cache.setColor((bgOverride ?? bg).rgba, (fgOverride ?? fg).rgba, adjustedColor ?? null);
499
512
  }
500
513
 
501
514
  if (adjustedColor) {
@@ -537,10 +550,3 @@ export class DomRendererRowFactory {
537
550
  (start[1] < end[1] && y === start[1] && x >= start[0]);
538
551
  }
539
552
  }
540
-
541
- function padStart(text: string, padChar: string, length: number): string {
542
- while (text.length < length) {
543
- text = padChar + text;
544
- }
545
- return text;
546
- }
@@ -3,6 +3,7 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
+ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
6
7
  import { IDisposable } from 'common/Types';
7
8
  import { FontWeight } from 'common/services/Services';
8
9
 
@@ -24,6 +25,10 @@ const enum FontVariant {
24
25
  BOLD_ITALIC = 3
25
26
  }
26
27
 
28
+ export interface IWidthCacheFontVariantCanvas {
29
+ setFont(fontFamily: string, fontSize: number, fontWeight: FontWeight, italic: boolean): void;
30
+ measure(c: string): number;
31
+ }
27
32
 
28
33
  export class WidthCache implements IDisposable {
29
34
  // flat cache for regular variant up to CacheSettings.FLAT_SIZE
@@ -42,50 +47,24 @@ export class WidthCache implements IDisposable {
42
47
  private _fontSize = 0;
43
48
  private _weight: FontWeight = 'normal';
44
49
  private _weightBold: FontWeight = 'bold';
45
- private _container: HTMLDivElement;
46
- private _measureElements: HTMLSpanElement[] = [];
47
-
48
- constructor(_document: Document, _helperContainer: HTMLElement) {
49
- this._container = _document.createElement('div');
50
- this._container.classList.add('xterm-width-cache-measure-container');
51
- this._container.setAttribute('aria-hidden', 'true');
52
- // SP should stack in spans
53
- this._container.style.whiteSpace = 'pre';
54
- // avoid undercuts in non-monospace fonts from kerning
55
- this._container.style.fontKerning = 'none';
56
-
57
- const regular = _document.createElement('span');
58
- regular.classList.add('xterm-char-measure-element');
59
-
60
- const bold = _document.createElement('span');
61
- bold.classList.add('xterm-char-measure-element');
62
- bold.style.fontWeight = 'bold';
63
-
64
- const italic = _document.createElement('span');
65
- italic.classList.add('xterm-char-measure-element');
66
- italic.style.fontStyle = 'italic';
67
-
68
- const boldItalic = _document.createElement('span');
69
- boldItalic.classList.add('xterm-char-measure-element');
70
- boldItalic.style.fontWeight = 'bold';
71
- boldItalic.style.fontStyle = 'italic';
72
-
73
- // NOTE: must be in order of FontVariant
74
- this._measureElements = [regular, bold, italic, boldItalic];
75
- this._container.appendChild(regular);
76
- this._container.appendChild(bold);
77
- this._container.appendChild(italic);
78
- this._container.appendChild(boldItalic);
79
-
80
- _helperContainer.appendChild(this._container);
50
+ private _canvasElements: IWidthCacheFontVariantCanvas[] = [];
51
+
52
+ constructor(
53
+ canvasFactory: () => IWidthCacheFontVariantCanvas = () => new WidthCacheFontVariantCanvas()
54
+ ) {
55
+ this._canvasElements = [
56
+ canvasFactory(),
57
+ canvasFactory(),
58
+ canvasFactory(),
59
+ canvasFactory()
60
+ ];
81
61
 
82
62
  this.clear();
83
63
  }
84
64
 
85
65
  public dispose(): void {
86
- this._container.remove(); // remove elements from DOM
87
- this._measureElements.length = 0; // release element refs
88
- this._holey = undefined; // free cache memory via GC
66
+ this._canvasElements.length = 0;
67
+ this._holey = undefined; // free cache memory via GC
89
68
  }
90
69
 
91
70
  /**
@@ -104,10 +83,11 @@ export class WidthCache implements IDisposable {
104
83
  */
105
84
  public setFont(font: string, fontSize: number, weight: FontWeight, weightBold: FontWeight): void {
106
85
  // skip if nothing changed
107
- if (font === this._font
108
- && fontSize === this._fontSize
109
- && weight === this._weight
110
- && weightBold === this._weightBold
86
+ if (
87
+ font === this._font &&
88
+ fontSize === this._fontSize &&
89
+ weight === this._weight &&
90
+ weightBold === this._weightBold
111
91
  ) {
112
92
  return;
113
93
  }
@@ -117,12 +97,10 @@ export class WidthCache implements IDisposable {
117
97
  this._weight = weight;
118
98
  this._weightBold = weightBold;
119
99
 
120
- this._container.style.fontFamily = this._font;
121
- this._container.style.fontSize = `${this._fontSize}px`;
122
- this._measureElements[FontVariant.REGULAR].style.fontWeight = `${weight}`;
123
- this._measureElements[FontVariant.BOLD].style.fontWeight = `${weightBold}`;
124
- this._measureElements[FontVariant.ITALIC].style.fontWeight = `${weight}`;
125
- this._measureElements[FontVariant.BOLD_ITALIC].style.fontWeight = `${weightBold}`;
100
+ this._canvasElements[FontVariant.REGULAR].setFont(font, fontSize, weight, false);
101
+ this._canvasElements[FontVariant.BOLD].setFont(font, fontSize, weightBold, false);
102
+ this._canvasElements[FontVariant.ITALIC].setFont(font, fontSize, weight, true);
103
+ this._canvasElements[FontVariant.BOLD_ITALIC].setFont(font, fontSize, weightBold, true);
126
104
 
127
105
  this.clear();
128
106
  }
@@ -160,8 +138,32 @@ export class WidthCache implements IDisposable {
160
138
  }
161
139
 
162
140
  protected _measure(c: string, variant: FontVariant): number {
163
- const el = this._measureElements[variant];
164
- el.textContent = c.repeat(WidthCacheSettings.REPEAT);
165
- return el.offsetWidth / WidthCacheSettings.REPEAT;
141
+ return this._canvasElements[variant].measure(c);
142
+ }
143
+ }
144
+
145
+ class WidthCacheFontVariantCanvas implements IWidthCacheFontVariantCanvas {
146
+ private _canvas: OffscreenCanvas | HTMLCanvasElement;
147
+ private _ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D;
148
+
149
+ constructor() {
150
+ if (typeof OffscreenCanvas !== 'undefined') {
151
+ this._canvas = new OffscreenCanvas(1, 1);
152
+ this._ctx = throwIfFalsy(this._canvas.getContext('2d'));
153
+ } else {
154
+ this._canvas = document.createElement('canvas');
155
+ this._canvas.width = 1;
156
+ this._canvas.height = 1;
157
+ this._ctx = throwIfFalsy(this._canvas.getContext('2d'));
158
+ }
159
+ }
160
+
161
+ public setFont(fontFamily: string, fontSize: number, fontWeight: FontWeight, italic: boolean): void {
162
+ const fontStyle = italic ? 'italic' : '';
163
+ this._ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`.trim();
164
+ }
165
+
166
+ public measure(c: string): number {
167
+ return this._ctx.measureText(c).width;
166
168
  }
167
169
  }
@@ -4,3 +4,10 @@
4
4
  */
5
5
 
6
6
  export const INVERTED_DEFAULT_COLOR = 257;
7
+
8
+ export const enum RendererConstants {
9
+ /**
10
+ * The idle time after which cursor blinking stops.
11
+ */
12
+ CURSOR_BLINK_IDLE_TIMEOUT = 5 * 60 * 1000
13
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Copyright (c) 2026 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { ICoreBrowserService } from 'browser/services/Services';
7
+ import { Disposable, toDisposable } from 'common/Lifecycle';
8
+ import { IOptionsService } from 'common/services/Services';
9
+
10
+ export class TextBlinkStateManager extends Disposable {
11
+ private _intervalDuration: number = 0;
12
+ private _interval: number | undefined;
13
+ private _blinkOn: boolean = true;
14
+ private _needsBlinkInViewport: boolean = false;
15
+ private _isViewportVisible: boolean = true;
16
+
17
+ constructor(
18
+ private readonly _renderCallback: () => void,
19
+ private readonly _coreBrowserService: ICoreBrowserService,
20
+ private readonly _optionsService: IOptionsService
21
+ ) {
22
+ super();
23
+ this._register(this._optionsService.onSpecificOptionChange('blinkIntervalDuration', duration => {
24
+ this.setIntervalDuration(duration);
25
+ }));
26
+ this.setIntervalDuration(this._optionsService.rawOptions.blinkIntervalDuration);
27
+ this._register(toDisposable(() => this._clearInterval()));
28
+ }
29
+
30
+ public get isBlinkOn(): boolean {
31
+ return this._blinkOn;
32
+ }
33
+
34
+ public get isEnabled(): boolean {
35
+ return this._intervalDuration > 0;
36
+ }
37
+
38
+ public setNeedsBlinkInViewport(needsBlinkInViewport: boolean): void {
39
+ if (this._needsBlinkInViewport === needsBlinkInViewport) {
40
+ return;
41
+ }
42
+
43
+ this._needsBlinkInViewport = needsBlinkInViewport;
44
+ this._updateIntervalState();
45
+ }
46
+
47
+ public setViewportVisible(isVisible: boolean): void {
48
+ if (this._isViewportVisible === isVisible) {
49
+ return;
50
+ }
51
+
52
+ this._isViewportVisible = isVisible;
53
+ this._updateIntervalState();
54
+ }
55
+
56
+ public setIntervalDuration(duration: number): void {
57
+ if (duration === this._intervalDuration) {
58
+ return;
59
+ }
60
+
61
+ this._intervalDuration = duration;
62
+ this._clearInterval();
63
+ this._updateIntervalState();
64
+ }
65
+
66
+ private _updateIntervalState(): void {
67
+ const shouldBlink = this._intervalDuration > 0 && this._needsBlinkInViewport && this._isViewportVisible;
68
+ if (shouldBlink) {
69
+ if (this._interval !== undefined) {
70
+ return;
71
+ }
72
+ const wasBlinkOn = this._blinkOn;
73
+ this._blinkOn = true;
74
+ this._interval = this._coreBrowserService.window.setInterval(() => {
75
+ this._blinkOn = !this._blinkOn;
76
+ this._renderCallback();
77
+ }, this._intervalDuration);
78
+ if (!wasBlinkOn) {
79
+ this._renderCallback();
80
+ }
81
+ return;
82
+ }
83
+
84
+ this._clearInterval();
85
+ if (!this._blinkOn) {
86
+ this._blinkOn = true;
87
+ this._renderCallback();
88
+ }
89
+ }
90
+
91
+ private _clearInterval(): void {
92
+ if (this._interval !== undefined) {
93
+ this._coreBrowserService.window.clearInterval(this._interval);
94
+ this._interval = undefined;
95
+ }
96
+ }
97
+ }
@@ -6,7 +6,7 @@
6
6
  import { Terminal } from '@xterm/xterm';
7
7
  import { ITerminal } from 'browser/Types';
8
8
  import { IDisposable } from 'common/Types';
9
- import type { Event } from 'vs/base/common/event';
9
+ import type { IEvent } from 'common/Event';
10
10
 
11
11
  export interface IDimensions {
12
12
  width: number;
@@ -39,6 +39,11 @@ export interface IRenderDimensions {
39
39
  export interface IRequestRedrawEvent {
40
40
  start: number;
41
41
  end: number;
42
+ /**
43
+ * Whether the redraw should happen synchronously. This is used to avoid
44
+ * flicker when the canvas is resized.
45
+ */
46
+ sync?: boolean;
42
47
  }
43
48
 
44
49
  /**
@@ -52,7 +57,7 @@ export interface IRenderer extends IDisposable {
52
57
  * Fires when the renderer is requesting to be redrawn on the next animation
53
58
  * frame but is _not_ a result of content changing (eg. selection changes).
54
59
  */
55
- readonly onRequestRedraw: Event<IRequestRedrawEvent>;
60
+ readonly onRequestRedraw: IEvent<IRequestRedrawEvent>;
56
61
 
57
62
  dispose(): void;
58
63
  handleDevicePixelRatioChange(): void;
@@ -60,6 +65,7 @@ export interface IRenderer extends IDisposable {
60
65
  handleCharSizeChanged(): void;
61
66
  handleBlur(): void;
62
67
  handleFocus(): void;
68
+ handleViewportVisibilityChange?(isVisible: boolean): void;
63
69
  handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void;
64
70
  handleCursorMove(): void;
65
71
  clear(): void;
@@ -0,0 +1,300 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+
6
+ import * as dom from '../Dom';
7
+ import { FastDomNode } from './fastDomNode';
8
+ import { GlobalPointerMoveMonitor } from './globalPointerMoveMonitor';
9
+ import { StandardWheelEvent } from './mouseEvent';
10
+ import { ScrollbarArrow, IScrollbarArrowOptions } from './scrollbarArrow';
11
+ import { ScrollbarState } from './scrollbarState';
12
+ import { ScrollbarVisibilityController } from './scrollbarVisibilityController';
13
+ import { Widget } from './widget';
14
+ import * as platform from 'common/Platform';
15
+ import { INewScrollPosition, Scrollable, ScrollbarVisibility } from './scrollable';
16
+
17
+ /**
18
+ * The orthogonal distance to the slider at which dragging "resets". This implements "snapping"
19
+ */
20
+ const POINTER_DRAG_RESET_DISTANCE = 140;
21
+
22
+ export interface ISimplifiedPointerEvent {
23
+ buttons: number;
24
+ pageX: number;
25
+ pageY: number;
26
+ }
27
+
28
+ export interface IScrollbarHost {
29
+ handleMouseWheel(mouseWheelEvent: StandardWheelEvent): void;
30
+ handleDragStart(): void;
31
+ handleDragEnd(): void;
32
+ }
33
+
34
+ interface IAbstractScrollbarOptions {
35
+ lazyRender: boolean;
36
+ host: IScrollbarHost;
37
+ scrollbarState: ScrollbarState;
38
+ visibility: ScrollbarVisibility;
39
+ extraScrollbarClassName: string;
40
+ scrollable: Scrollable;
41
+ scrollByPage: boolean;
42
+ }
43
+
44
+ export abstract class AbstractScrollbar extends Widget {
45
+
46
+ protected _host: IScrollbarHost;
47
+ protected _scrollable: Scrollable;
48
+ protected _scrollByPage: boolean;
49
+ private _lazyRender: boolean;
50
+ protected _scrollbarState: ScrollbarState;
51
+ protected _visibilityController: ScrollbarVisibilityController;
52
+ private _pointerMoveMonitor: GlobalPointerMoveMonitor;
53
+
54
+ public domNode: FastDomNode<HTMLElement>;
55
+ public slider!: FastDomNode<HTMLElement>;
56
+
57
+ protected _shouldRender: boolean;
58
+
59
+ constructor(opts: IAbstractScrollbarOptions) {
60
+ super();
61
+ this._lazyRender = opts.lazyRender;
62
+ this._host = opts.host;
63
+ this._scrollable = opts.scrollable;
64
+ this._scrollByPage = opts.scrollByPage;
65
+ this._scrollbarState = opts.scrollbarState;
66
+ this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'xterm-visible xterm-scrollbar ' + opts.extraScrollbarClassName, 'xterm-invisible xterm-scrollbar ' + opts.extraScrollbarClassName));
67
+ this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
68
+ this._pointerMoveMonitor = this._register(new GlobalPointerMoveMonitor());
69
+ this._shouldRender = true;
70
+ this.domNode = new FastDomNode(document.createElement('div'));
71
+ this.domNode.setAttribute('role', 'presentation');
72
+ this.domNode.setAttribute('aria-hidden', 'true');
73
+
74
+ this._visibilityController.setDomNode(this.domNode);
75
+ this.domNode.setPosition('absolute');
76
+
77
+ this._register(dom.addDisposableListener(this.domNode.domNode, dom.eventType.POINTER_DOWN, (e: PointerEvent) => this._domNodePointerDown(e)));
78
+ }
79
+
80
+ // ----------------- creation
81
+
82
+ /**
83
+ * Creates the dom node for an arrow & adds it to the container
84
+ */
85
+ protected _createArrow(opts: IScrollbarArrowOptions): ScrollbarArrow {
86
+ const arrow = this._register(new ScrollbarArrow(opts));
87
+ this.domNode.domNode.appendChild(arrow.bgDomNode);
88
+ this.domNode.domNode.appendChild(arrow.domNode);
89
+ return arrow;
90
+ }
91
+
92
+ /**
93
+ * Creates the slider dom node, adds it to the container & hooks up the events
94
+ */
95
+ protected _createSlider(top: number, left: number, width: number | undefined, height: number | undefined): void {
96
+ this.slider = new FastDomNode(document.createElement('div'));
97
+ this.slider.setClassName('xterm-slider');
98
+ this.slider.setPosition('absolute');
99
+ this.slider.setTop(top);
100
+ this.slider.setLeft(left);
101
+ if (typeof width === 'number') {
102
+ this.slider.setWidth(width);
103
+ }
104
+ if (typeof height === 'number') {
105
+ this.slider.setHeight(height);
106
+ }
107
+ this.slider.setLayerHinting(true);
108
+ this.slider.setContain('strict');
109
+
110
+ this.domNode.domNode.appendChild(this.slider.domNode);
111
+
112
+ this._register(dom.addDisposableListener(
113
+ this.slider.domNode,
114
+ dom.eventType.POINTER_DOWN,
115
+ (e: PointerEvent) => {
116
+ if (e.button === 0) {
117
+ e.preventDefault();
118
+ this._sliderPointerDown(e);
119
+ }
120
+ }
121
+ ));
122
+
123
+ this._onclick(this.slider.domNode, e => {
124
+ if (e.leftButton) {
125
+ e.stopPropagation();
126
+ }
127
+ });
128
+ }
129
+
130
+ // ----------------- Update state
131
+
132
+ protected _handleElementSize(visibleSize: number): boolean {
133
+ if (this._scrollbarState.setVisibleSize(visibleSize)) {
134
+ this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
135
+ this._shouldRender = true;
136
+ if (!this._lazyRender) {
137
+ this.render();
138
+ }
139
+ }
140
+ return this._shouldRender;
141
+ }
142
+
143
+ protected _handleElementScrollSize(elementScrollSize: number): boolean {
144
+ if (this._scrollbarState.setScrollSize(elementScrollSize)) {
145
+ this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
146
+ this._shouldRender = true;
147
+ if (!this._lazyRender) {
148
+ this.render();
149
+ }
150
+ }
151
+ return this._shouldRender;
152
+ }
153
+
154
+ protected _handleElementScrollPosition(elementScrollPosition: number): boolean {
155
+ if (this._scrollbarState.setScrollPosition(elementScrollPosition)) {
156
+ this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
157
+ this._shouldRender = true;
158
+ if (!this._lazyRender) {
159
+ this.render();
160
+ }
161
+ }
162
+ return this._shouldRender;
163
+ }
164
+
165
+ // ----------------- rendering
166
+
167
+ public beginReveal(): void {
168
+ this._visibilityController.setShouldBeVisible(true);
169
+ }
170
+
171
+ public beginHide(): void {
172
+ this._visibilityController.setShouldBeVisible(false);
173
+ }
174
+
175
+ public render(): void {
176
+ if (!this._shouldRender) {
177
+ return;
178
+ }
179
+ this._shouldRender = false;
180
+
181
+ this._renderDomNode(this._scrollbarState.getRectangleLargeSize(), this._scrollbarState.getRectangleSmallSize());
182
+ this._updateSlider(this._scrollbarState.getSliderSize(), this._scrollbarState.getArrowSize() + this._scrollbarState.getSliderPosition());
183
+ }
184
+ // ----------------- DOM events
185
+
186
+ private _domNodePointerDown(e: PointerEvent): void {
187
+ if (e.target !== this.domNode.domNode) {
188
+ return;
189
+ }
190
+ this._handlePointerDown(e);
191
+ }
192
+
193
+ public delegatePointerDown(e: PointerEvent): void {
194
+ const domTop = this.domNode.domNode.getClientRects()[0].top;
195
+ const sliderStart = domTop + this._scrollbarState.getSliderPosition();
196
+ const sliderStop = domTop + this._scrollbarState.getSliderPosition() + this._scrollbarState.getSliderSize();
197
+ const pointerPos = this._sliderPointerPosition(e);
198
+ if (sliderStart <= pointerPos && pointerPos <= sliderStop) {
199
+ if (e.button === 0) {
200
+ e.preventDefault();
201
+ this._sliderPointerDown(e);
202
+ }
203
+ } else {
204
+ this._handlePointerDown(e);
205
+ }
206
+ }
207
+
208
+ private _handlePointerDown(e: PointerEvent): void {
209
+ let offsetX: number;
210
+ let offsetY: number;
211
+ if (e.target === this.domNode.domNode && typeof e.offsetX === 'number' && typeof e.offsetY === 'number') {
212
+ offsetX = e.offsetX;
213
+ offsetY = e.offsetY;
214
+ } else {
215
+ const domNodePosition = dom.getDomNodePagePosition(this.domNode.domNode);
216
+ offsetX = e.pageX - domNodePosition.left;
217
+ offsetY = e.pageY - domNodePosition.top;
218
+ }
219
+
220
+ const offset = this._pointerDownRelativePosition(offsetX, offsetY);
221
+ this._setDesiredScrollPositionNow(
222
+ this._scrollByPage
223
+ ? this._scrollbarState.getDesiredScrollPositionFromOffsetPaged(offset)
224
+ : this._scrollbarState.getDesiredScrollPositionFromOffset(offset)
225
+ );
226
+
227
+ if (e.button === 0) {
228
+ e.preventDefault();
229
+ this._sliderPointerDown(e);
230
+ }
231
+ }
232
+
233
+ private _sliderPointerDown(e: PointerEvent): void {
234
+ if (!e.target || !(e.target instanceof Element)) {
235
+ return;
236
+ }
237
+ const initialPointerPosition = this._sliderPointerPosition(e);
238
+ const initialPointerOrthogonalPosition = this._sliderOrthogonalPointerPosition(e);
239
+ const initialScrollbarState = this._scrollbarState.clone();
240
+ this.slider.toggleClassName('xterm-active', true);
241
+
242
+ this._pointerMoveMonitor.startMonitoring(
243
+ e.target,
244
+ e.pointerId,
245
+ e.buttons,
246
+ (pointerMoveData: PointerEvent) => {
247
+ const pointerOrthogonalPosition = this._sliderOrthogonalPointerPosition(pointerMoveData);
248
+ const pointerOrthogonalDelta = Math.abs(pointerOrthogonalPosition - initialPointerOrthogonalPosition);
249
+
250
+ if (platform.isWindows && pointerOrthogonalDelta > POINTER_DRAG_RESET_DISTANCE) {
251
+ this._setDesiredScrollPositionNow(initialScrollbarState.getScrollPosition());
252
+ return;
253
+ }
254
+
255
+ const pointerPosition = this._sliderPointerPosition(pointerMoveData);
256
+ const pointerDelta = pointerPosition - initialPointerPosition;
257
+ this._setDesiredScrollPositionNow(initialScrollbarState.getDesiredScrollPositionFromDelta(pointerDelta));
258
+ },
259
+ () => {
260
+ this.slider.toggleClassName('xterm-active', false);
261
+ this._host.handleDragEnd();
262
+ }
263
+ );
264
+
265
+ this._host.handleDragStart();
266
+ }
267
+
268
+ private _setDesiredScrollPositionNow(_desiredScrollPosition: number): void {
269
+
270
+ const desiredScrollPosition: INewScrollPosition = {};
271
+ this.writeScrollPosition(desiredScrollPosition, _desiredScrollPosition);
272
+
273
+ this._scrollable.setScrollPositionNow(desiredScrollPosition);
274
+ }
275
+
276
+ public updateScrollbarSize(scrollbarSize: number): void {
277
+ this._updateScrollbarSize(scrollbarSize);
278
+ this._scrollbarState.setScrollbarSize(scrollbarSize);
279
+ this._shouldRender = true;
280
+ if (!this._lazyRender) {
281
+ this.render();
282
+ }
283
+ }
284
+
285
+ public isNeeded(): boolean {
286
+ return this._scrollbarState.isNeeded();
287
+ }
288
+
289
+ // ----------------- Overwrite these
290
+
291
+ protected abstract _renderDomNode(largeSize: number, smallSize: number): void;
292
+ protected abstract _updateSlider(sliderSize: number, sliderPosition: number): void;
293
+
294
+ protected abstract _pointerDownRelativePosition(offsetX: number, offsetY: number): number;
295
+ protected abstract _sliderPointerPosition(e: ISimplifiedPointerEvent): number;
296
+ protected abstract _sliderOrthogonalPointerPosition(e: ISimplifiedPointerEvent): number;
297
+ protected abstract _updateScrollbarSize(size: number): void;
298
+
299
+ public abstract writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void;
300
+ }