@xterm/xterm 6.1.0-beta.17 → 6.1.0-beta.171
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.
- package/README.md +27 -28
- package/css/xterm.css +29 -22
- package/lib/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/lib/xterm.mjs +8 -34
- package/lib/xterm.mjs.map +4 -4
- package/package.json +26 -15
- package/src/browser/AccessibilityManager.ts +6 -3
- package/src/browser/Clipboard.ts +6 -3
- package/src/browser/CoreBrowserTerminal.ts +142 -62
- package/src/browser/Dom.ts +178 -0
- package/src/browser/Linkifier.ts +3 -3
- package/src/browser/OscLinkProvider.ts +3 -1
- package/src/browser/RenderDebouncer.ts +2 -2
- package/src/browser/TimeBasedDebouncer.ts +2 -2
- package/src/browser/Types.ts +12 -11
- package/src/browser/Viewport.ts +40 -17
- package/src/browser/decorations/BufferDecorationRenderer.ts +1 -1
- package/src/browser/decorations/OverviewRulerRenderer.ts +15 -16
- package/src/browser/input/CompositionHelper.ts +10 -1
- package/src/browser/public/Terminal.ts +24 -27
- package/src/browser/renderer/dom/DomRenderer.ts +128 -8
- package/src/browser/renderer/dom/DomRendererRowFactory.ts +19 -13
- package/src/browser/renderer/dom/WidthCache.ts +54 -52
- package/src/browser/renderer/shared/Constants.ts +7 -0
- package/src/browser/renderer/shared/TextBlinkStateManager.ts +97 -0
- package/src/browser/renderer/shared/Types.ts +8 -2
- package/src/browser/scrollable/abstractScrollbar.ts +300 -0
- package/src/browser/scrollable/fastDomNode.ts +126 -0
- package/src/browser/scrollable/globalPointerMoveMonitor.ts +90 -0
- package/src/browser/scrollable/horizontalScrollbar.ts +85 -0
- package/src/browser/scrollable/mouseEvent.ts +292 -0
- package/src/browser/scrollable/scrollable.ts +486 -0
- package/src/browser/scrollable/scrollableElement.ts +579 -0
- package/src/browser/scrollable/scrollableElementOptions.ts +161 -0
- package/src/browser/scrollable/scrollbarArrow.ts +110 -0
- package/src/browser/scrollable/scrollbarState.ts +246 -0
- package/src/browser/scrollable/scrollbarVisibilityController.ts +113 -0
- package/src/browser/scrollable/touch.ts +481 -0
- package/src/browser/scrollable/verticalScrollbar.ts +143 -0
- package/src/browser/scrollable/widget.ts +23 -0
- package/src/browser/services/CharSizeService.ts +2 -2
- package/src/browser/services/CoreBrowserService.ts +7 -5
- package/src/browser/services/KeyboardService.ts +67 -0
- package/src/browser/services/LinkProviderService.ts +1 -1
- package/src/browser/services/MouseService.ts +2 -1
- package/src/browser/services/RenderService.ts +22 -15
- package/src/browser/services/SelectionService.ts +12 -4
- package/src/browser/services/Services.ts +24 -15
- package/src/browser/services/ThemeService.ts +2 -2
- package/src/common/Async.ts +105 -0
- package/src/common/CircularList.ts +2 -2
- package/src/common/Color.ts +8 -0
- package/src/common/CoreTerminal.ts +21 -11
- package/src/common/Event.ts +118 -0
- package/src/common/InputHandler.ts +244 -24
- package/src/common/Lifecycle.ts +113 -0
- package/src/common/Platform.ts +13 -3
- package/src/common/SortedList.ts +7 -3
- package/src/common/TaskQueue.ts +9 -3
- package/src/common/Types.ts +27 -7
- package/src/common/Version.ts +9 -0
- package/src/common/buffer/Buffer.ts +20 -14
- package/src/common/buffer/BufferLine.ts +4 -5
- package/src/common/buffer/BufferSet.ts +7 -6
- package/src/common/buffer/CellData.ts +57 -0
- package/src/common/buffer/Marker.ts +2 -2
- package/src/common/buffer/Types.ts +6 -2
- package/src/common/data/EscapeSequences.ts +71 -70
- package/src/common/input/Keyboard.ts +7 -6
- package/src/common/input/KittyKeyboard.ts +491 -0
- package/src/common/input/Win32InputMode.ts +297 -0
- package/src/common/input/WriteBuffer.ts +34 -2
- package/src/common/input/XParseColor.ts +2 -2
- package/src/common/parser/ApcParser.ts +245 -0
- package/src/common/parser/Constants.ts +22 -4
- package/src/common/parser/DcsParser.ts +5 -5
- package/src/common/parser/EscapeSequenceParser.ts +75 -22
- package/src/common/parser/OscParser.ts +5 -5
- package/src/common/parser/Types.ts +34 -1
- package/src/common/public/BufferLineApiView.ts +2 -2
- package/src/common/public/BufferNamespaceApi.ts +2 -2
- package/src/common/public/ParserApi.ts +3 -0
- package/src/common/services/BufferService.ts +8 -5
- package/src/common/services/CharsetService.ts +4 -0
- package/src/common/services/CoreMouseService.ts +2 -2
- package/src/common/services/CoreService.ts +18 -4
- package/src/common/services/DecorationService.ts +24 -8
- package/src/common/services/LogService.ts +1 -31
- package/src/common/services/OptionsService.ts +13 -4
- package/src/common/services/Services.ts +39 -16
- package/src/common/services/UnicodeService.ts +1 -1
- package/typings/xterm.d.ts +319 -35
- package/src/common/TypedArrayUtils.ts +0 -17
- package/src/vs/base/browser/browser.ts +0 -141
- package/src/vs/base/browser/canIUse.ts +0 -49
- package/src/vs/base/browser/dom.ts +0 -2369
- package/src/vs/base/browser/fastDomNode.ts +0 -316
- package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
- package/src/vs/base/browser/iframe.ts +0 -135
- package/src/vs/base/browser/keyboardEvent.ts +0 -213
- package/src/vs/base/browser/mouseEvent.ts +0 -229
- package/src/vs/base/browser/touch.ts +0 -372
- package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
- package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
- package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
- package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
- package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
- package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
- package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
- package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
- package/src/vs/base/browser/ui/widget.ts +0 -57
- package/src/vs/base/browser/window.ts +0 -14
- package/src/vs/base/common/arrays.ts +0 -887
- package/src/vs/base/common/arraysFind.ts +0 -202
- package/src/vs/base/common/assert.ts +0 -71
- package/src/vs/base/common/async.ts +0 -1992
- package/src/vs/base/common/cancellation.ts +0 -148
- package/src/vs/base/common/charCode.ts +0 -450
- package/src/vs/base/common/collections.ts +0 -140
- package/src/vs/base/common/decorators.ts +0 -130
- package/src/vs/base/common/equals.ts +0 -146
- package/src/vs/base/common/errors.ts +0 -303
- package/src/vs/base/common/event.ts +0 -1778
- package/src/vs/base/common/functional.ts +0 -32
- package/src/vs/base/common/hash.ts +0 -316
- package/src/vs/base/common/iterator.ts +0 -159
- package/src/vs/base/common/keyCodes.ts +0 -526
- package/src/vs/base/common/keybindings.ts +0 -284
- package/src/vs/base/common/lazy.ts +0 -47
- package/src/vs/base/common/lifecycle.ts +0 -801
- package/src/vs/base/common/linkedList.ts +0 -142
- package/src/vs/base/common/map.ts +0 -202
- package/src/vs/base/common/numbers.ts +0 -98
- package/src/vs/base/common/observable.ts +0 -76
- package/src/vs/base/common/observableInternal/api.ts +0 -31
- package/src/vs/base/common/observableInternal/autorun.ts +0 -281
- package/src/vs/base/common/observableInternal/base.ts +0 -489
- package/src/vs/base/common/observableInternal/debugName.ts +0 -145
- package/src/vs/base/common/observableInternal/derived.ts +0 -428
- package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
- package/src/vs/base/common/observableInternal/logging.ts +0 -328
- package/src/vs/base/common/observableInternal/promise.ts +0 -209
- package/src/vs/base/common/observableInternal/utils.ts +0 -610
- package/src/vs/base/common/platform.ts +0 -281
- package/src/vs/base/common/scrollable.ts +0 -522
- package/src/vs/base/common/sequence.ts +0 -34
- package/src/vs/base/common/stopwatch.ts +0 -43
- package/src/vs/base/common/strings.ts +0 -557
- package/src/vs/base/common/symbols.ts +0 -9
- package/src/vs/base/common/uint.ts +0 -59
- package/src/vs/patches/nls.ts +0 -90
- package/src/vs/typings/base-common.d.ts +0 -20
- package/src/vs/typings/require.d.ts +0 -42
- package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
- package/src/vs/typings/vscode-globals-product.d.ts +0 -33
|
@@ -5,16 +5,18 @@
|
|
|
5
5
|
|
|
6
6
|
import { DomRendererRowFactory, RowCss } from 'browser/renderer/dom/DomRendererRowFactory';
|
|
7
7
|
import { WidthCache } from 'browser/renderer/dom/WidthCache';
|
|
8
|
-
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
|
|
8
|
+
import { INVERTED_DEFAULT_COLOR, RendererConstants } from 'browser/renderer/shared/Constants';
|
|
9
9
|
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
|
|
10
10
|
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
|
|
11
|
+
import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager';
|
|
11
12
|
import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
|
12
13
|
import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
|
13
14
|
import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types';
|
|
14
15
|
import { color } from 'common/Color';
|
|
15
|
-
import { Disposable, toDisposable } from '
|
|
16
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
16
17
|
import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
|
17
|
-
import { Emitter } from '
|
|
18
|
+
import { Emitter } from 'common/Event';
|
|
19
|
+
import { addDisposableListener } from 'browser/Dom';
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
|
|
@@ -23,6 +25,7 @@ const FG_CLASS_PREFIX = 'xterm-fg-';
|
|
|
23
25
|
const BG_CLASS_PREFIX = 'xterm-bg-';
|
|
24
26
|
const FOCUS_CLASS = 'xterm-focus';
|
|
25
27
|
const SELECTION_CLASS = 'xterm-selection';
|
|
28
|
+
const CURSOR_BLINK_IDLE_CLASS = 'xterm-cursor-blink-idle';
|
|
26
29
|
|
|
27
30
|
let nextTerminalId = 1;
|
|
28
31
|
|
|
@@ -42,10 +45,15 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
42
45
|
private _selectionContainer: HTMLElement;
|
|
43
46
|
private _widthCache: WidthCache;
|
|
44
47
|
private _selectionRenderModel: ISelectionRenderModel = createSelectionRenderModel();
|
|
48
|
+
private _cursorBlinkStateManager: CursorBlinkStateManager;
|
|
49
|
+
private _textBlinkStateManager: TextBlinkStateManager;
|
|
50
|
+
private _rowHasBlinkingCells: boolean[] = [];
|
|
51
|
+
private _rowHasBlinkingCellsCount: number = 0;
|
|
45
52
|
|
|
46
53
|
public dimensions: IRenderDimensions;
|
|
47
54
|
|
|
48
|
-
|
|
55
|
+
private readonly _onRequestRedraw = this._register(new Emitter<IRequestRedrawEvent>());
|
|
56
|
+
public readonly onRequestRedraw = this._onRequestRedraw.event;
|
|
49
57
|
|
|
50
58
|
constructor(
|
|
51
59
|
private readonly _terminal: ITerminal,
|
|
@@ -89,6 +97,15 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
89
97
|
this._register(this._linkifier2.onShowLinkUnderline(e => this._handleLinkHover(e)));
|
|
90
98
|
this._register(this._linkifier2.onHideLinkUnderline(e => this._handleLinkLeave(e)));
|
|
91
99
|
|
|
100
|
+
this._cursorBlinkStateManager = new CursorBlinkStateManager(this._rowContainer, this._coreBrowserService);
|
|
101
|
+
this._register(addDisposableListener(this._document, 'mousedown', () => this._cursorBlinkStateManager.restartBlinkAnimation()));
|
|
102
|
+
this._register(toDisposable(() => this._cursorBlinkStateManager.dispose()));
|
|
103
|
+
this._textBlinkStateManager = this._register(new TextBlinkStateManager(
|
|
104
|
+
() => this._onRequestRedraw.fire({ start: 0, end: this._bufferService.rows - 1 }),
|
|
105
|
+
this._coreBrowserService,
|
|
106
|
+
this._optionsService
|
|
107
|
+
));
|
|
108
|
+
|
|
92
109
|
this._register(toDisposable(() => {
|
|
93
110
|
this._element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
|
94
111
|
|
|
@@ -101,7 +118,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
101
118
|
this._dimensionsStyleElement.remove();
|
|
102
119
|
}));
|
|
103
120
|
|
|
104
|
-
this._widthCache = new WidthCache(
|
|
121
|
+
this._widthCache = new WidthCache();
|
|
105
122
|
this._widthCache.setFont(
|
|
106
123
|
this._optionsService.rawOptions.fontFamily,
|
|
107
124
|
this._optionsService.rawOptions.fontSize,
|
|
@@ -186,6 +203,9 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
186
203
|
`}` +
|
|
187
204
|
`${this._terminalSelector} span.${RowCss.ITALIC_CLASS} {` +
|
|
188
205
|
` font-style: italic;` +
|
|
206
|
+
`}` +
|
|
207
|
+
`${this._terminalSelector} span.${RowCss.BLINK_HIDDEN_CLASS} {` +
|
|
208
|
+
` visibility: hidden;` +
|
|
189
209
|
`}`;
|
|
190
210
|
// Blink animation
|
|
191
211
|
const blinkAnimationUnderlineId = `blink_underline_${this._terminalClass}`;
|
|
@@ -225,6 +245,10 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
225
245
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
|
226
246
|
` animation: ${blinkAnimationBlockId} 1s step-end infinite;` +
|
|
227
247
|
`}` +
|
|
248
|
+
// Disable cursor blinking when idle
|
|
249
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` +
|
|
250
|
+
` animation: none !important;` +
|
|
251
|
+
`}` +
|
|
228
252
|
// !important helps fix an issue where the cursor will not render on top of the selection,
|
|
229
253
|
// however it's very hard to fix this issue and retain the blink animation without the use of
|
|
230
254
|
// !important. So this edge case fails when cursor blink is on.
|
|
@@ -307,10 +331,14 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
307
331
|
const row = this._document.createElement('div');
|
|
308
332
|
this._rowContainer.appendChild(row);
|
|
309
333
|
this._rowElements.push(row);
|
|
334
|
+
this._rowHasBlinkingCells.push(false);
|
|
310
335
|
}
|
|
311
336
|
// Remove excess elements
|
|
312
337
|
while (this._rowElements.length > rows) {
|
|
313
338
|
this._rowContainer.removeChild(this._rowElements.pop()!);
|
|
339
|
+
if (this._rowHasBlinkingCells.pop()) {
|
|
340
|
+
this._rowHasBlinkingCellsCount--;
|
|
341
|
+
}
|
|
314
342
|
}
|
|
315
343
|
}
|
|
316
344
|
|
|
@@ -328,14 +356,20 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
328
356
|
|
|
329
357
|
public handleBlur(): void {
|
|
330
358
|
this._rowContainer.classList.remove(FOCUS_CLASS);
|
|
359
|
+
this._cursorBlinkStateManager.pause();
|
|
331
360
|
this.renderRows(0, this._bufferService.rows - 1);
|
|
332
361
|
}
|
|
333
362
|
|
|
334
363
|
public handleFocus(): void {
|
|
335
364
|
this._rowContainer.classList.add(FOCUS_CLASS);
|
|
365
|
+
this._cursorBlinkStateManager.resume();
|
|
336
366
|
this.renderRows(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
|
337
367
|
}
|
|
338
368
|
|
|
369
|
+
public handleViewportVisibilityChange(isVisible: boolean): void {
|
|
370
|
+
this._textBlinkStateManager.setViewportVisible(isVisible);
|
|
371
|
+
}
|
|
372
|
+
|
|
339
373
|
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
|
|
340
374
|
// Remove all selections
|
|
341
375
|
this._selectionContainer.replaceChildren();
|
|
@@ -406,7 +440,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
406
440
|
}
|
|
407
441
|
|
|
408
442
|
public handleCursorMove(): void {
|
|
409
|
-
//
|
|
443
|
+
// Reset idle timer on cursor movement (which happens on input)
|
|
444
|
+
this._cursorBlinkStateManager.restartBlinkAnimation();
|
|
410
445
|
}
|
|
411
446
|
|
|
412
447
|
private _handleOptionsChanged(): void {
|
|
@@ -436,6 +471,11 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
436
471
|
*/
|
|
437
472
|
e.replaceChildren();
|
|
438
473
|
}
|
|
474
|
+
if (this._rowHasBlinkingCellsCount > 0) {
|
|
475
|
+
this._rowHasBlinkingCells.fill(false);
|
|
476
|
+
this._rowHasBlinkingCellsCount = 0;
|
|
477
|
+
this._textBlinkStateManager.setNeedsBlinkInViewport(false);
|
|
478
|
+
}
|
|
439
479
|
}
|
|
440
480
|
|
|
441
481
|
public renderRows(start: number, end: number): void {
|
|
@@ -445,6 +485,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
445
485
|
const cursorBlink = this._coreService.decPrivateModes.cursorBlink ?? this._optionsService.rawOptions.cursorBlink;
|
|
446
486
|
const cursorStyle = this._coreService.decPrivateModes.cursorStyle ?? this._optionsService.rawOptions.cursorStyle;
|
|
447
487
|
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
|
|
488
|
+
const rowInfo = { hasBlinkingCells: false };
|
|
448
489
|
|
|
449
490
|
for (let y = start; y <= end; y++) {
|
|
450
491
|
const row = y + buffer.ydisp;
|
|
@@ -462,13 +503,17 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
462
503
|
cursorInactiveStyle,
|
|
463
504
|
cursorX,
|
|
464
505
|
cursorBlink,
|
|
506
|
+
this._textBlinkStateManager.isBlinkOn,
|
|
465
507
|
this.dimensions.css.cell.width,
|
|
466
508
|
this._widthCache,
|
|
467
509
|
-1,
|
|
468
|
-
-1
|
|
510
|
+
-1,
|
|
511
|
+
rowInfo
|
|
469
512
|
)
|
|
470
513
|
);
|
|
514
|
+
this._setRowBlinkState(y, rowInfo.hasBlinkingCells);
|
|
471
515
|
}
|
|
516
|
+
this._updateTextBlinkState();
|
|
472
517
|
}
|
|
473
518
|
|
|
474
519
|
private get _terminalSelector(): string {
|
|
@@ -513,6 +558,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
513
558
|
const cursorBlink = this._optionsService.rawOptions.cursorBlink;
|
|
514
559
|
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
|
|
515
560
|
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
|
|
561
|
+
const rowInfo = { hasBlinkingCells: false };
|
|
516
562
|
|
|
517
563
|
// refresh rows within link range
|
|
518
564
|
for (let i = y; i <= y2; ++i) {
|
|
@@ -531,12 +577,86 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
531
577
|
cursorInactiveStyle,
|
|
532
578
|
cursorX,
|
|
533
579
|
cursorBlink,
|
|
580
|
+
this._textBlinkStateManager.isBlinkOn,
|
|
534
581
|
this.dimensions.css.cell.width,
|
|
535
582
|
this._widthCache,
|
|
536
583
|
enabled ? (i === y ? x : 0) : -1,
|
|
537
|
-
enabled ? ((i === y2 ? x2 : cols) - 1) : -1
|
|
584
|
+
enabled ? ((i === y2 ? x2 : cols) - 1) : -1,
|
|
585
|
+
rowInfo
|
|
538
586
|
)
|
|
539
587
|
);
|
|
588
|
+
this._setRowBlinkState(i, rowInfo.hasBlinkingCells);
|
|
540
589
|
}
|
|
590
|
+
this._updateTextBlinkState();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private _setRowBlinkState(row: number, hasBlinkingCells: boolean): void {
|
|
594
|
+
const previous = this._rowHasBlinkingCells[row];
|
|
595
|
+
if (previous === hasBlinkingCells) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
this._rowHasBlinkingCells[row] = hasBlinkingCells;
|
|
599
|
+
this._rowHasBlinkingCellsCount += hasBlinkingCells ? 1 : -1;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
private _updateTextBlinkState(): void {
|
|
603
|
+
this._textBlinkStateManager.setNeedsBlinkInViewport(this._rowHasBlinkingCellsCount > 0);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
class CursorBlinkStateManager {
|
|
608
|
+
private _idleTimeout: number | undefined;
|
|
609
|
+
private _isIdlePaused: boolean = false;
|
|
610
|
+
|
|
611
|
+
constructor(
|
|
612
|
+
private readonly _rowContainer: HTMLElement,
|
|
613
|
+
private readonly _coreBrowserService: ICoreBrowserService
|
|
614
|
+
) {
|
|
615
|
+
if (this._coreBrowserService.isFocused) {
|
|
616
|
+
this._resetIdleTimer();
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
public dispose(): void {
|
|
621
|
+
this._clearIdleTimer();
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
public restartBlinkAnimation(): void {
|
|
625
|
+
if (this._isIdlePaused) {
|
|
626
|
+
this._rowContainer.classList.remove(CURSOR_BLINK_IDLE_CLASS);
|
|
627
|
+
}
|
|
628
|
+
this._resetIdleTimer();
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
public pause(): void {
|
|
632
|
+
this._isIdlePaused = false;
|
|
633
|
+
this._clearIdleTimer();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
public resume(): void {
|
|
637
|
+
this._isIdlePaused = false;
|
|
638
|
+
this._rowContainer.classList.remove(CURSOR_BLINK_IDLE_CLASS);
|
|
639
|
+
this._resetIdleTimer();
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
private _resetIdleTimer(): void {
|
|
643
|
+
this._isIdlePaused = false;
|
|
644
|
+
this._clearIdleTimer();
|
|
645
|
+
this._idleTimeout = this._coreBrowserService.window.setTimeout(() => {
|
|
646
|
+
this._stopBlinkingDueToIdle();
|
|
647
|
+
}, RendererConstants.CURSOR_BLINK_IDLE_TIMEOUT);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
private _clearIdleTimer(): void {
|
|
651
|
+
if (this._idleTimeout) {
|
|
652
|
+
this._coreBrowserService.window.clearTimeout(this._idleTimeout);
|
|
653
|
+
this._idleTimeout = undefined;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
private _stopBlinkingDueToIdle(): void {
|
|
658
|
+
this._rowContainer.classList.add(CURSOR_BLINK_IDLE_CLASS);
|
|
659
|
+
this._isIdlePaused = true;
|
|
660
|
+
this._idleTimeout = undefined;
|
|
541
661
|
}
|
|
542
662
|
}
|
|
@@ -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:#${
|
|
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:#${
|
|
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
|
|
498
|
-
cache.setColor((bgOverride
|
|
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
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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.
|
|
87
|
-
this.
|
|
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 (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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.
|
|
121
|
-
this.
|
|
122
|
-
this.
|
|
123
|
-
this.
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
}
|
|
@@ -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 {
|
|
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:
|
|
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;
|