@xterm/xterm 6.1.0-beta.28 → 6.1.0-beta.281

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 (181) 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 +56 -45
  8. package/src/browser/AccessibilityManager.ts +18 -13
  9. package/src/browser/Clipboard.ts +8 -5
  10. package/src/browser/ColorContrastCache.ts +3 -3
  11. package/src/browser/CoreBrowserTerminal.ts +179 -348
  12. package/src/browser/Dom.ts +178 -0
  13. package/src/browser/Linkifier.ts +14 -14
  14. package/src/browser/OscLinkProvider.ts +86 -18
  15. package/src/browser/RenderDebouncer.ts +7 -9
  16. package/src/browser/TimeBasedDebouncer.ts +12 -5
  17. package/src/browser/Types.ts +16 -14
  18. package/src/browser/Viewport.ts +58 -23
  19. package/src/browser/decorations/BufferDecorationRenderer.ts +3 -3
  20. package/src/browser/decorations/ColorZoneStore.ts +1 -1
  21. package/src/browser/decorations/OverviewRulerRenderer.ts +36 -20
  22. package/src/browser/input/CompositionHelper.ts +47 -11
  23. package/src/browser/input/Mouse.ts +5 -9
  24. package/src/browser/input/MoveToCell.ts +3 -3
  25. package/src/browser/public/Terminal.ts +33 -36
  26. package/src/browser/renderer/dom/DomRenderer.ts +265 -89
  27. package/src/browser/renderer/dom/DomRendererRowFactory.ts +34 -27
  28. package/src/browser/renderer/dom/WidthCache.ts +57 -55
  29. package/src/browser/renderer/shared/Constants.ts +7 -0
  30. package/src/browser/renderer/shared/RendererUtils.ts +1 -1
  31. package/src/browser/renderer/shared/SelectionRenderModel.ts +2 -2
  32. package/src/browser/renderer/shared/TextBlinkStateManager.ts +97 -0
  33. package/src/browser/renderer/shared/Types.ts +10 -4
  34. package/src/browser/scrollable/abstractScrollbar.ts +300 -0
  35. package/src/browser/scrollable/fastDomNode.ts +126 -0
  36. package/src/browser/scrollable/globalPointerMoveMonitor.ts +90 -0
  37. package/src/browser/scrollable/horizontalScrollbar.ts +85 -0
  38. package/src/browser/scrollable/mouseEvent.ts +292 -0
  39. package/src/browser/scrollable/scrollable.ts +486 -0
  40. package/src/browser/scrollable/scrollableElement.ts +581 -0
  41. package/src/browser/scrollable/scrollableElementOptions.ts +161 -0
  42. package/src/browser/scrollable/scrollbarArrow.ts +110 -0
  43. package/src/browser/scrollable/scrollbarState.ts +246 -0
  44. package/src/browser/scrollable/scrollbarVisibilityController.ts +113 -0
  45. package/src/browser/scrollable/touch.ts +485 -0
  46. package/src/browser/scrollable/verticalScrollbar.ts +143 -0
  47. package/src/browser/scrollable/widget.ts +23 -0
  48. package/src/browser/selection/SelectionModel.ts +1 -1
  49. package/src/browser/services/CharSizeService.ts +4 -4
  50. package/src/browser/services/CharacterJoinerService.ts +13 -11
  51. package/src/browser/services/CoreBrowserService.ts +7 -5
  52. package/src/browser/services/KeyboardService.ts +67 -0
  53. package/src/browser/services/LinkProviderService.ts +3 -3
  54. package/src/browser/services/MouseCoordsService.ts +47 -0
  55. package/src/browser/services/MouseService.ts +619 -23
  56. package/src/browser/services/RenderService.ts +33 -21
  57. package/src/browser/services/SelectionService.ts +72 -54
  58. package/src/browser/services/Services.ts +44 -21
  59. package/src/browser/services/ThemeService.ts +9 -9
  60. package/src/common/Async.ts +141 -0
  61. package/src/common/CircularList.ts +24 -3
  62. package/src/common/Color.ts +13 -5
  63. package/src/common/CoreTerminal.ts +61 -35
  64. package/src/common/Event.ts +118 -0
  65. package/src/common/InputHandler.ts +286 -105
  66. package/src/common/Lifecycle.ts +113 -0
  67. package/src/common/Platform.ts +15 -5
  68. package/src/common/SortedList.ts +8 -4
  69. package/src/common/StringBuilder.ts +67 -0
  70. package/src/common/TaskQueue.ts +17 -8
  71. package/src/common/Types.ts +68 -206
  72. package/src/common/Version.ts +1 -1
  73. package/src/common/WindowsMode.ts +2 -2
  74. package/src/common/buffer/AttributeData.ts +3 -2
  75. package/src/common/buffer/Buffer.ts +47 -32
  76. package/src/common/buffer/BufferLine.ts +175 -100
  77. package/src/common/buffer/BufferLineStringCache.ts +69 -0
  78. package/src/common/buffer/BufferReflow.ts +7 -4
  79. package/src/common/buffer/BufferSet.ts +13 -9
  80. package/src/common/buffer/CellData.ts +61 -4
  81. package/src/common/buffer/Constants.ts +13 -13
  82. package/src/common/buffer/Marker.ts +4 -4
  83. package/src/common/buffer/Types.ts +153 -3
  84. package/src/common/data/Charsets.ts +4 -7
  85. package/src/common/data/EscapeSequences.ts +71 -70
  86. package/src/common/input/Keyboard.ts +17 -10
  87. package/src/common/input/KittyKeyboard.ts +526 -0
  88. package/src/common/input/TextDecoder.ts +3 -3
  89. package/src/common/input/UnicodeV6.ts +3 -2
  90. package/src/common/input/Win32InputMode.ts +297 -0
  91. package/src/common/input/WriteBuffer.ts +108 -39
  92. package/src/common/input/XParseColor.ts +2 -2
  93. package/src/common/parser/ApcParser.ts +196 -0
  94. package/src/common/parser/Constants.ts +14 -4
  95. package/src/common/parser/DcsParser.ts +15 -16
  96. package/src/common/parser/EscapeSequenceParser.ts +214 -72
  97. package/src/common/parser/OscParser.ts +14 -15
  98. package/src/common/parser/Params.ts +31 -12
  99. package/src/common/parser/Types.ts +40 -30
  100. package/src/common/public/BufferApiView.ts +3 -3
  101. package/src/common/public/BufferLineApiView.ts +4 -4
  102. package/src/common/public/BufferNamespaceApi.ts +5 -5
  103. package/src/common/public/ParserApi.ts +5 -2
  104. package/src/common/public/UnicodeApi.ts +1 -1
  105. package/src/common/services/BufferService.ts +17 -13
  106. package/src/common/services/CharsetService.ts +6 -2
  107. package/src/common/services/CoreService.ts +23 -10
  108. package/src/common/services/DecorationService.ts +260 -15
  109. package/src/common/services/InstantiationService.ts +2 -2
  110. package/src/common/services/LogService.ts +2 -32
  111. package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +22 -133
  112. package/src/common/services/OptionsService.ts +17 -7
  113. package/src/common/services/OscLinkService.ts +3 -2
  114. package/src/common/services/ServiceRegistry.ts +14 -8
  115. package/src/common/services/Services.ts +54 -50
  116. package/src/common/services/UnicodeService.ts +6 -11
  117. package/typings/xterm.d.ts +329 -34
  118. package/src/common/Clone.ts +0 -23
  119. package/src/common/TypedArrayUtils.ts +0 -17
  120. package/src/vs/base/browser/browser.ts +0 -141
  121. package/src/vs/base/browser/canIUse.ts +0 -49
  122. package/src/vs/base/browser/dom.ts +0 -2369
  123. package/src/vs/base/browser/fastDomNode.ts +0 -316
  124. package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
  125. package/src/vs/base/browser/iframe.ts +0 -135
  126. package/src/vs/base/browser/keyboardEvent.ts +0 -213
  127. package/src/vs/base/browser/mouseEvent.ts +0 -229
  128. package/src/vs/base/browser/touch.ts +0 -372
  129. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
  130. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
  131. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
  132. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
  133. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
  134. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
  135. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
  136. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
  137. package/src/vs/base/browser/ui/widget.ts +0 -57
  138. package/src/vs/base/browser/window.ts +0 -14
  139. package/src/vs/base/common/arrays.ts +0 -887
  140. package/src/vs/base/common/arraysFind.ts +0 -202
  141. package/src/vs/base/common/assert.ts +0 -71
  142. package/src/vs/base/common/async.ts +0 -1992
  143. package/src/vs/base/common/cancellation.ts +0 -148
  144. package/src/vs/base/common/charCode.ts +0 -450
  145. package/src/vs/base/common/collections.ts +0 -140
  146. package/src/vs/base/common/decorators.ts +0 -130
  147. package/src/vs/base/common/equals.ts +0 -146
  148. package/src/vs/base/common/errors.ts +0 -303
  149. package/src/vs/base/common/event.ts +0 -1778
  150. package/src/vs/base/common/functional.ts +0 -32
  151. package/src/vs/base/common/hash.ts +0 -316
  152. package/src/vs/base/common/iterator.ts +0 -159
  153. package/src/vs/base/common/keyCodes.ts +0 -526
  154. package/src/vs/base/common/keybindings.ts +0 -284
  155. package/src/vs/base/common/lazy.ts +0 -47
  156. package/src/vs/base/common/lifecycle.ts +0 -801
  157. package/src/vs/base/common/linkedList.ts +0 -142
  158. package/src/vs/base/common/map.ts +0 -202
  159. package/src/vs/base/common/numbers.ts +0 -98
  160. package/src/vs/base/common/observable.ts +0 -76
  161. package/src/vs/base/common/observableInternal/api.ts +0 -31
  162. package/src/vs/base/common/observableInternal/autorun.ts +0 -281
  163. package/src/vs/base/common/observableInternal/base.ts +0 -489
  164. package/src/vs/base/common/observableInternal/debugName.ts +0 -145
  165. package/src/vs/base/common/observableInternal/derived.ts +0 -428
  166. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
  167. package/src/vs/base/common/observableInternal/logging.ts +0 -328
  168. package/src/vs/base/common/observableInternal/promise.ts +0 -209
  169. package/src/vs/base/common/observableInternal/utils.ts +0 -610
  170. package/src/vs/base/common/platform.ts +0 -281
  171. package/src/vs/base/common/scrollable.ts +0 -522
  172. package/src/vs/base/common/sequence.ts +0 -34
  173. package/src/vs/base/common/stopwatch.ts +0 -43
  174. package/src/vs/base/common/strings.ts +0 -557
  175. package/src/vs/base/common/symbols.ts +0 -9
  176. package/src/vs/base/common/uint.ts +0 -59
  177. package/src/vs/patches/nls.ts +0 -90
  178. package/src/vs/typings/base-common.d.ts +0 -20
  179. package/src/vs/typings/require.d.ts +0 -42
  180. package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
  181. package/src/vs/typings/vscode-globals-product.d.ts +0 -33
@@ -3,26 +3,31 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
- import { DomRendererRowFactory, RowCss } from 'browser/renderer/dom/DomRendererRowFactory';
7
- import { WidthCache } from 'browser/renderer/dom/WidthCache';
8
- import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
9
- import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
10
- import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
11
- import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from 'browser/renderer/shared/Types';
12
- import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
13
- import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types';
14
- import { color } from 'common/Color';
15
- import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
16
- import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from 'common/services/Services';
17
- import { Emitter } from 'vs/base/common/event';
18
-
19
-
20
- const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
21
- const ROW_CONTAINER_CLASS = 'xterm-rows';
22
- const FG_CLASS_PREFIX = 'xterm-fg-';
23
- const BG_CLASS_PREFIX = 'xterm-bg-';
24
- const FOCUS_CLASS = 'xterm-focus';
25
- const SELECTION_CLASS = 'xterm-selection';
6
+ import { DomRendererRowFactory, RowCss } from './DomRendererRowFactory';
7
+ import { WidthCache } from './WidthCache';
8
+ import { INVERTED_DEFAULT_COLOR, RendererConstants } from '../shared/Constants';
9
+ import { createRenderDimensions } from '../shared/RendererUtils';
10
+ import { createSelectionRenderModel } from '../shared/SelectionRenderModel';
11
+ import { TextBlinkStateManager } from '../shared/TextBlinkStateManager';
12
+ import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from '../shared/Types';
13
+ import { ICharSizeService, ICoreBrowserService, IThemeService } from '../../services/Services';
14
+ import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from '../../Types';
15
+ import { color } from '../../../common/Color';
16
+ import { Disposable, toDisposable } from '../../../common/Lifecycle';
17
+ import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from '../../../common/services/Services';
18
+ import { Emitter } from '../../../common/Event';
19
+ import { addDisposableListener } from '../../Dom';
20
+
21
+
22
+ const enum Constants {
23
+ TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-',
24
+ ROW_CONTAINER_CLASS = 'xterm-rows',
25
+ FG_CLASS_PREFIX = 'xterm-fg-',
26
+ BG_CLASS_PREFIX = 'xterm-bg-',
27
+ FOCUS_CLASS = 'xterm-focus',
28
+ SELECTION_CLASS = 'xterm-selection',
29
+ CURSOR_BLINK_IDLE_CLASS = 'xterm-cursor-blink-idle'
30
+ }
26
31
 
27
32
  let nextTerminalId = 1;
28
33
 
@@ -42,10 +47,18 @@ export class DomRenderer extends Disposable implements IRenderer {
42
47
  private _selectionContainer: HTMLElement;
43
48
  private _widthCache: WidthCache;
44
49
  private _selectionRenderModel: ISelectionRenderModel = createSelectionRenderModel();
50
+ private _lastSelectionStart: [number, number] | undefined;
51
+ private _lastSelectionEnd: [number, number] | undefined;
52
+ private _lastSelectionColumnMode: boolean = false;
53
+ private _cursorBlinkStateManager: CursorBlinkStateManager;
54
+ private _textBlinkStateManager: TextBlinkStateManager;
55
+ private _rowHasBlinkingCells: boolean[] = [];
56
+ private _rowHasBlinkingCellsCount: number = 0;
45
57
 
46
58
  public dimensions: IRenderDimensions;
47
59
 
48
- public readonly onRequestRedraw = this._register(new Emitter<IRequestRedrawEvent>()).event;
60
+ private readonly _onRequestRedraw = this._register(new Emitter<IRequestRedrawEvent>());
61
+ public readonly onRequestRedraw = this._onRequestRedraw.event;
49
62
 
50
63
  constructor(
51
64
  private readonly _terminal: ITerminal,
@@ -65,12 +78,12 @@ export class DomRenderer extends Disposable implements IRenderer {
65
78
  ) {
66
79
  super();
67
80
  this._rowContainer = this._document.createElement('div');
68
- this._rowContainer.classList.add(ROW_CONTAINER_CLASS);
81
+ this._rowContainer.classList.add(Constants.ROW_CONTAINER_CLASS);
69
82
  this._rowContainer.style.lineHeight = 'normal';
70
83
  this._rowContainer.setAttribute('aria-hidden', 'true');
71
84
  this._refreshRowElements(this._bufferService.cols, this._bufferService.rows);
72
85
  this._selectionContainer = this._document.createElement('div');
73
- this._selectionContainer.classList.add(SELECTION_CLASS);
86
+ this._selectionContainer.classList.add(Constants.SELECTION_CLASS);
74
87
  this._selectionContainer.setAttribute('aria-hidden', 'true');
75
88
 
76
89
  this.dimensions = createRenderDimensions();
@@ -82,15 +95,24 @@ export class DomRenderer extends Disposable implements IRenderer {
82
95
 
83
96
  this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document);
84
97
 
85
- this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass);
98
+ this._element.classList.add(Constants.TERMINAL_CLASS_PREFIX + this._terminalClass);
86
99
  this._screenElement.appendChild(this._rowContainer);
87
100
  this._screenElement.appendChild(this._selectionContainer);
88
101
 
89
102
  this._register(this._linkifier2.onShowLinkUnderline(e => this._handleLinkHover(e)));
90
103
  this._register(this._linkifier2.onHideLinkUnderline(e => this._handleLinkLeave(e)));
91
104
 
105
+ this._cursorBlinkStateManager = new CursorBlinkStateManager(this._rowContainer, this._coreBrowserService);
106
+ this._register(addDisposableListener(this._document, 'mousedown', () => this._cursorBlinkStateManager.restartBlinkAnimation()));
107
+ this._register(toDisposable(() => this._cursorBlinkStateManager.dispose()));
108
+ this._textBlinkStateManager = this._register(new TextBlinkStateManager(
109
+ () => this._onRequestRedraw.fire({ start: 0, end: this._bufferService.rows - 1 }),
110
+ this._coreBrowserService,
111
+ this._optionsService
112
+ ));
113
+
92
114
  this._register(toDisposable(() => {
93
- this._element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass);
115
+ this._element.classList.remove(Constants.TERMINAL_CLASS_PREFIX + this._terminalClass);
94
116
 
95
117
  // Outside influences such as React unmounts may manipulate the DOM before our disposal.
96
118
  // https://github.com/xtermjs/xterm.js/issues/2960
@@ -101,7 +123,7 @@ export class DomRenderer extends Disposable implements IRenderer {
101
123
  this._dimensionsStyleElement.remove();
102
124
  }));
103
125
 
104
- this._widthCache = new WidthCache(this._document, this._helperContainer);
126
+ this._widthCache = new WidthCache();
105
127
  this._widthCache.setFont(
106
128
  this._optionsService.rawOptions.fontFamily,
107
129
  this._optionsService.rawOptions.fontSize,
@@ -140,7 +162,7 @@ export class DomRenderer extends Disposable implements IRenderer {
140
162
  }
141
163
 
142
164
  const styles =
143
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` +
165
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} span {` +
144
166
  ` display: inline-block;` + // TODO: find workaround for inline-block (creates ~20% render penalty)
145
167
  ` height: 100%;` +
146
168
  ` vertical-align: top;` +
@@ -161,19 +183,22 @@ export class DomRenderer extends Disposable implements IRenderer {
161
183
 
162
184
  // Base CSS
163
185
  let styles =
164
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
186
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} {` +
165
187
  // Disabling pointer events circumvents a browser behavior that prevents `click` events from
166
188
  // being delivered if the target element is replaced during the click. This happened due to
167
189
  // refresh() being called during the mousedown handler to start a selection.
168
190
  ` pointer-events: none;` +
169
191
  ` color: ${colors.foreground.css};` +
192
+ `}`;
193
+ styles +=
194
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}, ${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} span {` +
170
195
  ` font-family: ${this._optionsService.rawOptions.fontFamily};` +
171
196
  ` font-size: ${this._optionsService.rawOptions.fontSize}px;` +
172
197
  ` font-kerning: none;` +
173
198
  ` white-space: pre` +
174
199
  `}`;
175
200
  styles +=
176
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .xterm-dim {` +
201
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .xterm-dim {` +
177
202
  ` color: ${color.multiplyOpacity(colors.foreground, 0.5).css};` +
178
203
  `}`;
179
204
  // Text styles
@@ -186,6 +211,9 @@ export class DomRenderer extends Disposable implements IRenderer {
186
211
  `}` +
187
212
  `${this._terminalSelector} span.${RowCss.ITALIC_CLASS} {` +
188
213
  ` font-style: italic;` +
214
+ `}` +
215
+ `${this._terminalSelector} span.${RowCss.BLINK_HIDDEN_CLASS} {` +
216
+ ` visibility: hidden;` +
189
217
  `}`;
190
218
  // Blink animation
191
219
  const blinkAnimationUnderlineId = `blink_underline_${this._terminalClass}`;
@@ -216,66 +244,70 @@ export class DomRenderer extends Disposable implements IRenderer {
216
244
  `}`;
217
245
  // Cursor
218
246
  styles +=
219
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` +
247
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` +
220
248
  ` animation: ${blinkAnimationUnderlineId} 1s step-end infinite;` +
221
249
  `}` +
222
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
250
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
223
251
  ` animation: ${blinkAnimationBarId} 1s step-end infinite;` +
224
252
  `}` +
225
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
253
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
226
254
  ` animation: ${blinkAnimationBlockId} 1s step-end infinite;` +
227
255
  `}` +
256
+ // Disable cursor blinking when idle
257
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` +
258
+ ` animation: none !important;` +
259
+ `}` +
228
260
  // !important helps fix an issue where the cursor will not render on top of the selection,
229
261
  // however it's very hard to fix this issue and retain the blink animation without the use of
230
262
  // !important. So this edge case fails when cursor blink is on.
231
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
263
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
232
264
  ` background-color: ${colors.cursor.css};` +
233
265
  ` color: ${colors.cursorAccent.css};` +
234
266
  `}` +
235
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS}:not(.${RowCss.CURSOR_BLINK_CLASS}) {` +
267
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS}:not(.${RowCss.CURSOR_BLINK_CLASS}) {` +
236
268
  ` background-color: ${colors.cursor.css} !important;` +
237
269
  ` color: ${colors.cursorAccent.css} !important;` +
238
270
  `}` +
239
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_OUTLINE_CLASS} {` +
271
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_OUTLINE_CLASS} {` +
240
272
  ` outline: 1px solid ${colors.cursor.css};` +
241
273
  ` outline-offset: -1px;` +
242
274
  `}` +
243
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
275
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
244
276
  ` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` +
245
277
  `}` +
246
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` +
278
+ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` +
247
279
  ` border-bottom: 1px ${colors.cursor.css};` +
248
280
  ` border-bottom-style: solid;` +
249
281
  ` height: calc(100% - 1px);` +
250
282
  `}`;
251
283
  // Selection
252
284
  styles +=
253
- `${this._terminalSelector} .${SELECTION_CLASS} {` +
285
+ `${this._terminalSelector} .${Constants.SELECTION_CLASS} {` +
254
286
  ` position: absolute;` +
255
287
  ` top: 0;` +
256
288
  ` left: 0;` +
257
289
  ` z-index: 1;` +
258
290
  ` pointer-events: none;` +
259
291
  `}` +
260
- `${this._terminalSelector}.focus .${SELECTION_CLASS} div {` +
292
+ `${this._terminalSelector}.focus .${Constants.SELECTION_CLASS} div {` +
261
293
  ` position: absolute;` +
262
294
  ` background-color: ${colors.selectionBackgroundOpaque.css};` +
263
295
  `}` +
264
- `${this._terminalSelector} .${SELECTION_CLASS} div {` +
296
+ `${this._terminalSelector} .${Constants.SELECTION_CLASS} div {` +
265
297
  ` position: absolute;` +
266
298
  ` background-color: ${colors.selectionInactiveBackgroundOpaque.css};` +
267
299
  `}`;
268
300
  // Colors
269
301
  for (const [i, c] of colors.ansi.entries()) {
270
302
  styles +=
271
- `${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
272
- `${this._terminalSelector} .${FG_CLASS_PREFIX}${i}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(c, 0.5).css}; }` +
273
- `${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
303
+ `${this._terminalSelector} .${Constants.FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
304
+ `${this._terminalSelector} .${Constants.FG_CLASS_PREFIX}${i}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(c, 0.5).css}; }` +
305
+ `${this._terminalSelector} .${Constants.BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
274
306
  }
275
307
  styles +=
276
- `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(colors.background).css}; }` +
277
- `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(color.opaque(colors.background), 0.5).css}; }` +
278
- `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${colors.foreground.css}; }`;
308
+ `${this._terminalSelector} .${Constants.FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(colors.background).css}; }` +
309
+ `${this._terminalSelector} .${Constants.FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(color.opaque(colors.background), 0.5).css}; }` +
310
+ `${this._terminalSelector} .${Constants.BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${colors.foreground.css}; }`;
279
311
 
280
312
  this._themeStyleElement.textContent = styles;
281
313
  }
@@ -307,10 +339,14 @@ export class DomRenderer extends Disposable implements IRenderer {
307
339
  const row = this._document.createElement('div');
308
340
  this._rowContainer.appendChild(row);
309
341
  this._rowElements.push(row);
342
+ this._rowHasBlinkingCells.push(false);
310
343
  }
311
344
  // Remove excess elements
312
345
  while (this._rowElements.length > rows) {
313
346
  this._rowContainer.removeChild(this._rowElements.pop()!);
347
+ if (this._rowHasBlinkingCells.pop()) {
348
+ this._rowHasBlinkingCellsCount--;
349
+ }
314
350
  }
315
351
  }
316
352
 
@@ -327,61 +363,105 @@ export class DomRenderer extends Disposable implements IRenderer {
327
363
  }
328
364
 
329
365
  public handleBlur(): void {
330
- this._rowContainer.classList.remove(FOCUS_CLASS);
366
+ this._rowContainer.classList.remove(Constants.FOCUS_CLASS);
367
+ this._cursorBlinkStateManager.pause();
331
368
  this.renderRows(0, this._bufferService.rows - 1);
332
369
  }
333
370
 
334
371
  public handleFocus(): void {
335
- this._rowContainer.classList.add(FOCUS_CLASS);
372
+ this._rowContainer.classList.add(Constants.FOCUS_CLASS);
373
+ this._cursorBlinkStateManager.resume();
336
374
  this.renderRows(this._bufferService.buffer.y, this._bufferService.buffer.y);
337
375
  }
338
376
 
377
+ public handleViewportVisibilityChange(isVisible: boolean): void {
378
+ this._textBlinkStateManager.setViewportVisible(isVisible);
379
+ }
380
+
339
381
  public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
382
+ const rows = this._bufferService.rows;
383
+
340
384
  // Remove all selections
341
385
  this._selectionContainer.replaceChildren();
342
386
  this._rowFactory.handleSelectionChanged(start, end, columnSelectMode);
343
- this.renderRows(0, this._bufferService.rows - 1);
344
387
 
345
- // Selection does not exist
388
+ // Determine old selection viewport band
389
+ let oldViewportStart = 0;
390
+ let oldViewportEnd = -1;
391
+ if (this._lastSelectionStart && this._lastSelectionEnd) {
392
+ this._selectionRenderModel.update(this._terminal, this._lastSelectionStart, this._lastSelectionEnd, this._lastSelectionColumnMode);
393
+ if (this._selectionRenderModel.hasSelection) {
394
+ oldViewportStart = this._selectionRenderModel.viewportCappedStartRow;
395
+ oldViewportEnd = this._selectionRenderModel.viewportCappedEndRow;
396
+ }
397
+ }
398
+
399
+ // Determine new selection viewport band and create overlays
400
+ let newViewportStart = 0;
401
+ let newViewportEnd = -1;
346
402
  if (!start || !end) {
347
403
  return;
348
404
  }
349
-
350
405
  this._selectionRenderModel.update(this._terminal, start, end, columnSelectMode);
351
- if (!this._selectionRenderModel.hasSelection) {
352
- return;
406
+ if (this._selectionRenderModel.hasSelection) {
407
+ const viewportStartRow = this._selectionRenderModel.viewportStartRow;
408
+ const viewportEndRow = this._selectionRenderModel.viewportEndRow;
409
+ const viewportCappedStartRow = this._selectionRenderModel.viewportCappedStartRow;
410
+ const viewportCappedEndRow = this._selectionRenderModel.viewportCappedEndRow;
411
+
412
+ newViewportStart = viewportCappedStartRow;
413
+ newViewportEnd = viewportCappedEndRow;
414
+
415
+ // Create the selections
416
+ const documentFragment = this._document.createDocumentFragment();
417
+
418
+ if (columnSelectMode) {
419
+ const isXFlipped = start[0] > end[0];
420
+ documentFragment.appendChild(
421
+ this._createSelectionElement(viewportCappedStartRow, isXFlipped ? end[0] : start[0], isXFlipped ? start[0] : end[0], viewportCappedEndRow - viewportCappedStartRow + 1)
422
+ );
423
+ } else {
424
+ // Draw first row
425
+ const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
426
+ const endCol = viewportCappedStartRow === viewportEndRow ? end[0] : this._bufferService.cols;
427
+ documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol));
428
+ // Draw middle rows
429
+ const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1;
430
+ documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._bufferService.cols, middleRowsCount));
431
+ // Draw final row
432
+ if (viewportCappedStartRow !== viewportCappedEndRow) {
433
+ // Only draw viewportEndRow if it's not the same as viewporttartRow
434
+ const finalEndCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
435
+ documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, finalEndCol));
436
+ }
437
+ }
438
+ this._selectionContainer.appendChild(documentFragment);
353
439
  }
354
440
 
355
- // Translate from buffer position to viewport position
356
- const viewportStartRow = this._selectionRenderModel.viewportStartRow;
357
- const viewportEndRow = this._selectionRenderModel.viewportEndRow;
358
- const viewportCappedStartRow = this._selectionRenderModel.viewportCappedStartRow;
359
- const viewportCappedEndRow = this._selectionRenderModel.viewportCappedEndRow;
360
-
361
- // Create the selections
362
- const documentFragment = this._document.createDocumentFragment();
363
-
364
- if (columnSelectMode) {
365
- const isXFlipped = start[0] > end[0];
366
- documentFragment.appendChild(
367
- this._createSelectionElement(viewportCappedStartRow, isXFlipped ? end[0] : start[0], isXFlipped ? start[0] : end[0], viewportCappedEndRow - viewportCappedStartRow + 1)
368
- );
369
- } else {
370
- // Draw first row
371
- const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
372
- const endCol = viewportCappedStartRow === viewportEndRow ? end[0] : this._bufferService.cols;
373
- documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol));
374
- // Draw middle rows
375
- const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1;
376
- documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._bufferService.cols, middleRowsCount));
377
- // Draw final row
378
- if (viewportCappedStartRow !== viewportCappedEndRow) {
379
- // Only draw viewportEndRow if it's not the same as viewporttartRow
380
- const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
381
- documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol));
441
+ // Compute minimal row range to redraw
442
+ let renderStartRow = Math.min(oldViewportStart, newViewportStart);
443
+ let renderEndRow = Math.max(oldViewportEnd, newViewportEnd);
444
+
445
+ if (renderEndRow >= 0) {
446
+ // Clamp to viewport
447
+ renderStartRow = Math.max(renderStartRow, 0);
448
+ renderEndRow = Math.min(renderEndRow, rows - 1);
449
+
450
+ // Ensure cursor row is included when a selection is present
451
+ const buffer = this._bufferService.buffer;
452
+ const cursorViewportRow = buffer.y;
453
+ if (this._selectionRenderModel.hasSelection && cursorViewportRow >= 0 && cursorViewportRow < rows) {
454
+ renderStartRow = Math.min(renderStartRow, cursorViewportRow);
455
+ renderEndRow = Math.max(renderEndRow, cursorViewportRow);
382
456
  }
457
+
458
+ this.renderRows(renderStartRow, renderEndRow);
383
459
  }
384
- this._selectionContainer.appendChild(documentFragment);
460
+
461
+ // Update last selection state
462
+ this._lastSelectionStart = start;
463
+ this._lastSelectionEnd = end;
464
+ this._lastSelectionColumnMode = columnSelectMode;
385
465
  }
386
466
 
387
467
  /**
@@ -406,7 +486,8 @@ export class DomRenderer extends Disposable implements IRenderer {
406
486
  }
407
487
 
408
488
  public handleCursorMove(): void {
409
- // No-op, the cursor is drawn when rows are drawn
489
+ // Reset idle timer on cursor movement (which happens on input)
490
+ this._cursorBlinkStateManager.restartBlinkAnimation();
410
491
  }
411
492
 
412
493
  private _handleOptionsChanged(): void {
@@ -436,6 +517,11 @@ export class DomRenderer extends Disposable implements IRenderer {
436
517
  */
437
518
  e.replaceChildren();
438
519
  }
520
+ if (this._rowHasBlinkingCellsCount > 0) {
521
+ this._rowHasBlinkingCells.fill(false);
522
+ this._rowHasBlinkingCellsCount = 0;
523
+ this._textBlinkStateManager.setNeedsBlinkInViewport(false);
524
+ }
439
525
  }
440
526
 
441
527
  public renderRows(start: number, end: number): void {
@@ -445,13 +531,19 @@ export class DomRenderer extends Disposable implements IRenderer {
445
531
  const cursorBlink = this._coreService.decPrivateModes.cursorBlink ?? this._optionsService.rawOptions.cursorBlink;
446
532
  const cursorStyle = this._coreService.decPrivateModes.cursorStyle ?? this._optionsService.rawOptions.cursorStyle;
447
533
  const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
534
+ const rowInfo = { hasBlinkingCells: false };
448
535
 
449
536
  for (let y = start; y <= end; y++) {
450
537
  const row = y + buffer.ydisp;
451
538
  const rowElement = this._rowElements[y];
539
+ if (!rowElement) {
540
+ continue;
541
+ }
452
542
  const lineData = buffer.lines.get(row);
453
- if (!rowElement || !lineData) {
454
- break;
543
+ if (!lineData) {
544
+ rowElement.replaceChildren();
545
+ this._setRowBlinkState(y, false);
546
+ continue;
455
547
  }
456
548
  rowElement.replaceChildren(
457
549
  ...this._rowFactory.createRow(
@@ -462,17 +554,21 @@ export class DomRenderer extends Disposable implements IRenderer {
462
554
  cursorInactiveStyle,
463
555
  cursorX,
464
556
  cursorBlink,
557
+ this._textBlinkStateManager.isBlinkOn,
465
558
  this.dimensions.css.cell.width,
466
559
  this._widthCache,
467
560
  -1,
468
- -1
561
+ -1,
562
+ rowInfo
469
563
  )
470
564
  );
565
+ this._setRowBlinkState(y, rowInfo.hasBlinkingCells);
471
566
  }
567
+ this._updateTextBlinkState();
472
568
  }
473
569
 
474
570
  private get _terminalSelector(): string {
475
- return `.${TERMINAL_CLASS_PREFIX}${this._terminalClass}`;
571
+ return `.${Constants.TERMINAL_CLASS_PREFIX}${this._terminalClass}`;
476
572
  }
477
573
 
478
574
  private _handleLinkHover(e: ILinkifierEvent): void {
@@ -513,14 +609,20 @@ export class DomRenderer extends Disposable implements IRenderer {
513
609
  const cursorBlink = this._optionsService.rawOptions.cursorBlink;
514
610
  const cursorStyle = this._optionsService.rawOptions.cursorStyle;
515
611
  const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
612
+ const rowInfo = { hasBlinkingCells: false };
516
613
 
517
614
  // refresh rows within link range
518
615
  for (let i = y; i <= y2; ++i) {
519
616
  const row = i + buffer.ydisp;
520
617
  const rowElement = this._rowElements[i];
618
+ if (!rowElement) {
619
+ continue;
620
+ }
521
621
  const bufferline = buffer.lines.get(row);
522
- if (!rowElement || !bufferline) {
523
- break;
622
+ if (!bufferline) {
623
+ rowElement.replaceChildren();
624
+ this._setRowBlinkState(i, false);
625
+ continue;
524
626
  }
525
627
  rowElement.replaceChildren(
526
628
  ...this._rowFactory.createRow(
@@ -531,12 +633,86 @@ export class DomRenderer extends Disposable implements IRenderer {
531
633
  cursorInactiveStyle,
532
634
  cursorX,
533
635
  cursorBlink,
636
+ this._textBlinkStateManager.isBlinkOn,
534
637
  this.dimensions.css.cell.width,
535
638
  this._widthCache,
536
639
  enabled ? (i === y ? x : 0) : -1,
537
- enabled ? ((i === y2 ? x2 : cols) - 1) : -1
640
+ enabled ? ((i === y2 ? x2 : cols) - 1) : -1,
641
+ rowInfo
538
642
  )
539
643
  );
644
+ this._setRowBlinkState(i, rowInfo.hasBlinkingCells);
540
645
  }
646
+ this._updateTextBlinkState();
647
+ }
648
+
649
+ private _setRowBlinkState(row: number, hasBlinkingCells: boolean): void {
650
+ const previous = this._rowHasBlinkingCells[row];
651
+ if (previous === hasBlinkingCells) {
652
+ return;
653
+ }
654
+ this._rowHasBlinkingCells[row] = hasBlinkingCells;
655
+ this._rowHasBlinkingCellsCount += hasBlinkingCells ? 1 : -1;
656
+ }
657
+
658
+ private _updateTextBlinkState(): void {
659
+ this._textBlinkStateManager.setNeedsBlinkInViewport(this._rowHasBlinkingCellsCount > 0);
660
+ }
661
+ }
662
+
663
+ class CursorBlinkStateManager {
664
+ private _idleTimeout: number | undefined;
665
+ private _isIdlePaused: boolean = false;
666
+
667
+ constructor(
668
+ private readonly _rowContainer: HTMLElement,
669
+ private readonly _coreBrowserService: ICoreBrowserService
670
+ ) {
671
+ if (this._coreBrowserService.isFocused) {
672
+ this._resetIdleTimer();
673
+ }
674
+ }
675
+
676
+ public dispose(): void {
677
+ this._clearIdleTimer();
678
+ }
679
+
680
+ public restartBlinkAnimation(): void {
681
+ if (this._isIdlePaused) {
682
+ this._rowContainer.classList.remove(Constants.CURSOR_BLINK_IDLE_CLASS);
683
+ }
684
+ this._resetIdleTimer();
685
+ }
686
+
687
+ public pause(): void {
688
+ this._isIdlePaused = false;
689
+ this._clearIdleTimer();
690
+ }
691
+
692
+ public resume(): void {
693
+ this._isIdlePaused = false;
694
+ this._rowContainer.classList.remove(Constants.CURSOR_BLINK_IDLE_CLASS);
695
+ this._resetIdleTimer();
696
+ }
697
+
698
+ private _resetIdleTimer(): void {
699
+ this._isIdlePaused = false;
700
+ this._clearIdleTimer();
701
+ this._idleTimeout = this._coreBrowserService.window.setTimeout(() => {
702
+ this._stopBlinkingDueToIdle();
703
+ }, RendererConstants.CURSOR_BLINK_IDLE_TIMEOUT);
704
+ }
705
+
706
+ private _clearIdleTimer(): void {
707
+ if (this._idleTimeout !== undefined) {
708
+ this._coreBrowserService.window.clearTimeout(this._idleTimeout);
709
+ this._idleTimeout = undefined;
710
+ }
711
+ }
712
+
713
+ private _stopBlinkingDueToIdle(): void {
714
+ this._rowContainer.classList.add(Constants.CURSOR_BLINK_IDLE_CLASS);
715
+ this._isIdlePaused = true;
716
+ this._idleTimeout = undefined;
541
717
  }
542
718
  }