@xterm/xterm 6.1.0-beta.9 → 6.1.0-beta.91
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 +5 -11
- package/lib/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/lib/xterm.mjs +17 -17
- package/lib/xterm.mjs.map +4 -4
- package/package.json +26 -19
- package/src/browser/AccessibilityManager.ts +4 -1
- package/src/browser/CoreBrowserTerminal.ts +49 -8
- package/src/browser/OscLinkProvider.ts +1 -1
- package/src/browser/Types.ts +4 -1
- package/src/browser/Viewport.ts +16 -1
- package/src/browser/input/CompositionHelper.ts +10 -1
- package/src/browser/public/Terminal.ts +6 -5
- package/src/browser/renderer/dom/DomRenderer.ts +74 -3
- package/src/browser/renderer/dom/WidthCache.ts +54 -52
- package/src/browser/renderer/shared/Constants.ts +7 -0
- package/src/browser/renderer/shared/Types.ts +5 -0
- package/src/browser/services/MouseService.ts +2 -1
- package/src/browser/services/RenderService.ts +9 -5
- package/src/browser/services/Services.ts +1 -1
- package/src/common/Color.ts +8 -0
- package/src/common/CoreTerminal.ts +2 -1
- package/src/common/InputHandler.ts +52 -9
- package/src/common/Platform.ts +4 -1
- package/src/common/Types.ts +1 -1
- package/src/common/Version.ts +9 -0
- package/src/common/buffer/Buffer.ts +4 -0
- package/src/common/buffer/Types.ts +4 -0
- package/src/common/data/Charsets.ts +1 -1
- package/src/common/input/Keyboard.ts +7 -6
- package/src/common/services/CharsetService.ts +4 -0
- package/src/common/services/DecorationService.ts +17 -3
- package/src/common/services/OptionsService.ts +2 -2
- package/src/common/services/Services.ts +6 -1
- package/typings/xterm.d.ts +165 -34
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xterm/xterm",
|
|
3
3
|
"description": "Full xterm terminal, in your browser",
|
|
4
|
-
"version": "6.1.0-beta.
|
|
4
|
+
"version": "6.1.0-beta.91",
|
|
5
5
|
"main": "lib/xterm.js",
|
|
6
6
|
"module": "lib/xterm.mjs",
|
|
7
7
|
"style": "css/xterm.css",
|
|
@@ -27,8 +27,11 @@
|
|
|
27
27
|
"xterm"
|
|
28
28
|
],
|
|
29
29
|
"scripts": {
|
|
30
|
-
"
|
|
30
|
+
"presetup": "npm run build",
|
|
31
|
+
"setup": "npm run esbuild",
|
|
32
|
+
"postsetup": "npm run esbuild-demo-server",
|
|
31
33
|
"start": "node demo/start",
|
|
34
|
+
"dev": "concurrently -k -p [{name}] -n tsc,esbuild,esbuild-demo-client,esbuild-demo-server,server -c blue,yellow,cyan,green,magenta \"npm:tsc-watch\" \"npm:esbuild-watch\" \"npm:esbuild-demo-client-watch\" \"npm:esbuild-demo-server-watch\" \"npm:start\"",
|
|
32
35
|
"build": "npm run tsc",
|
|
33
36
|
"watch": "npm run tsc-watch",
|
|
34
37
|
"tsc": "tsc -b ./tsconfig.all.json",
|
|
@@ -38,13 +41,15 @@
|
|
|
38
41
|
"esbuild-package": "node bin/esbuild_all.mjs --prod",
|
|
39
42
|
"esbuild-package-watch": "node bin/esbuild_all.mjs --prod --watch",
|
|
40
43
|
"esbuild-package-headless-only": "node bin/esbuild.mjs --prod --headless",
|
|
41
|
-
"esbuild-demo": "node bin/esbuild.mjs --demo-client",
|
|
42
|
-
"esbuild-demo-watch": "node bin/esbuild.mjs --demo-client --watch",
|
|
44
|
+
"esbuild-demo-client": "node bin/esbuild.mjs --demo-client",
|
|
45
|
+
"esbuild-demo-client-watch": "node bin/esbuild.mjs --demo-client --watch",
|
|
46
|
+
"esbuild-demo-server": "node bin/esbuild.mjs --demo-server",
|
|
47
|
+
"esbuild-demo-server-watch": "node bin/esbuild.mjs --demo-server --watch",
|
|
43
48
|
"test": "npm run test-unit",
|
|
44
49
|
"posttest": "npm run lint",
|
|
45
|
-
"lint": "eslint
|
|
46
|
-
"lint-fix": "eslint
|
|
47
|
-
"lint-api": "eslint --
|
|
50
|
+
"lint": "eslint --max-warnings 0 src/ addons/ demo/",
|
|
51
|
+
"lint-fix": "eslint --fix src/ addons/ demo/",
|
|
52
|
+
"lint-api": "eslint --config eslint.config.typings.mjs --max-warnings 0 typings/",
|
|
48
53
|
"test-unit": "node ./bin/test_unit.js",
|
|
49
54
|
"test-unit-coverage": "node ./bin/test_unit.js --coverage",
|
|
50
55
|
"test-unit-dev": "cross-env NODE_PATH='./out' mocha",
|
|
@@ -68,44 +73,46 @@
|
|
|
68
73
|
},
|
|
69
74
|
"devDependencies": {
|
|
70
75
|
"@lunapaint/png-codec": "^0.2.0",
|
|
71
|
-
"@playwright/test": "^1.
|
|
72
|
-
"@stylistic/eslint-plugin": "^
|
|
76
|
+
"@playwright/test": "^1.57.0",
|
|
77
|
+
"@stylistic/eslint-plugin": "^4.4.1",
|
|
73
78
|
"@types/chai": "^4.2.22",
|
|
74
79
|
"@types/debug": "^4.1.7",
|
|
75
80
|
"@types/deep-equal": "^1.0.1",
|
|
76
81
|
"@types/express": "4",
|
|
77
82
|
"@types/express-ws": "^3.0.1",
|
|
78
|
-
"@types/jsdom": "^
|
|
83
|
+
"@types/jsdom": "^27.0.0",
|
|
79
84
|
"@types/mocha": "^9.0.0",
|
|
80
|
-
"@types/node": "^
|
|
85
|
+
"@types/node": "^22.19.3",
|
|
81
86
|
"@types/trusted-types": "^1.0.6",
|
|
82
87
|
"@types/utf8": "^3.0.0",
|
|
83
88
|
"@types/webpack": "^5.28.0",
|
|
84
89
|
"@types/ws": "^8.2.0",
|
|
85
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
86
|
-
"@typescript-eslint/parser": "^
|
|
90
|
+
"@typescript-eslint/eslint-plugin": "^8.50.1",
|
|
91
|
+
"@typescript-eslint/parser": "^8.50.1",
|
|
87
92
|
"chai": "^4.3.4",
|
|
93
|
+
"concurrently": "^9.1.2",
|
|
88
94
|
"cross-env": "^7.0.3",
|
|
89
95
|
"deep-equal": "^2.0.5",
|
|
90
96
|
"esbuild": "~0.25.2",
|
|
91
|
-
"eslint": "^
|
|
92
|
-
"eslint-plugin-jsdoc": "^
|
|
97
|
+
"eslint": "^9.39.2",
|
|
98
|
+
"eslint-plugin-jsdoc": "^50.8.0",
|
|
93
99
|
"express": "^4.19.2",
|
|
94
100
|
"express-ws": "^5.0.2",
|
|
95
|
-
"jsdom": "^
|
|
101
|
+
"jsdom": "^27.3.0",
|
|
96
102
|
"mocha": "^10.1.0",
|
|
97
103
|
"mustache": "^4.2.0",
|
|
98
104
|
"node-pty": "1.1.0-beta19",
|
|
99
|
-
"nyc": "^
|
|
105
|
+
"nyc": "^17.1.0",
|
|
100
106
|
"source-map-loader": "^3.0.0",
|
|
101
107
|
"source-map-support": "^0.5.20",
|
|
102
108
|
"ts-loader": "^9.3.1",
|
|
103
|
-
"typescript": "5.
|
|
109
|
+
"typescript": "^5.9.3",
|
|
110
|
+
"typescript-eslint": "^8.50.1",
|
|
104
111
|
"utf8": "^3.0.0",
|
|
105
112
|
"webpack": "^5.61.0",
|
|
106
113
|
"webpack-cli": "^4.9.1",
|
|
107
114
|
"ws": "^8.2.3",
|
|
108
115
|
"xterm-benchmark": "^0.3.1"
|
|
109
116
|
},
|
|
110
|
-
"commit": "
|
|
117
|
+
"commit": "94a264679e6bd51b7021ca59e3b2e1e80d02c633"
|
|
111
118
|
}
|
|
@@ -151,7 +151,7 @@ export class AccessibilityManager extends Disposable {
|
|
|
151
151
|
if (char === '\n') {
|
|
152
152
|
this._liveRegionLineCount++;
|
|
153
153
|
if (this._liveRegionLineCount === MAX_ROWS_TO_READ + 1) {
|
|
154
|
-
this._liveRegion.textContent
|
|
154
|
+
this._liveRegion.textContent = Strings.tooMuchOutput.get();
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
}
|
|
@@ -203,6 +203,9 @@ export class AccessibilityManager extends Disposable {
|
|
|
203
203
|
if (this._charsToAnnounce.length === 0) {
|
|
204
204
|
return;
|
|
205
205
|
}
|
|
206
|
+
if (this._liveRegion.textContent === Strings.tooMuchOutput.get()) {
|
|
207
|
+
this._clearLiveRegion();
|
|
208
|
+
}
|
|
206
209
|
this._liveRegion.textContent += this._charsToAnnounce;
|
|
207
210
|
this._charsToAnnounce = '';
|
|
208
211
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* http://linux.die.net/man/7/urxvt
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker } from '@xterm/xterm';
|
|
24
|
+
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker, IRenderDimensions as IRenderDimensionsApi } from '@xterm/xterm';
|
|
25
25
|
import { copyHandler, handlePasteEvent, moveTextAreaUnderMouseCursor, paste, rightClickHandler } from 'browser/Clipboard';
|
|
26
26
|
import * as Strings from 'browser/LocalizableStrings';
|
|
27
27
|
import { OscLinkProvider } from 'browser/OscLinkProvider';
|
|
@@ -126,8 +126,6 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
126
126
|
public readonly onCursorMove = this._onCursorMove.event;
|
|
127
127
|
private readonly _onKey = this._register(new Emitter<{ key: string, domEvent: KeyboardEvent }>());
|
|
128
128
|
public readonly onKey = this._onKey.event;
|
|
129
|
-
private readonly _onRender = this._register(new Emitter<{ start: number, end: number }>());
|
|
130
|
-
public readonly onRender = this._onRender.event;
|
|
131
129
|
private readonly _onSelectionChange = this._register(new Emitter<void>());
|
|
132
130
|
public readonly onSelectionChange = this._onSelectionChange.event;
|
|
133
131
|
private readonly _onTitleChange = this._register(new Emitter<string>());
|
|
@@ -145,6 +143,26 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
145
143
|
public get onA11yTab(): Event<number> { return this._onA11yTabEmitter.event; }
|
|
146
144
|
private _onWillOpen = this._register(new Emitter<HTMLElement>());
|
|
147
145
|
public get onWillOpen(): Event<HTMLElement> { return this._onWillOpen.event; }
|
|
146
|
+
private readonly _onDimensionsChange = this._register(new Emitter<IRenderDimensionsApi>());
|
|
147
|
+
public readonly onDimensionsChange = this._onDimensionsChange.event;
|
|
148
|
+
|
|
149
|
+
public get dimensions(): IRenderDimensionsApi | undefined {
|
|
150
|
+
if (!this._renderService) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
const dimensions = this._renderService.dimensions;
|
|
154
|
+
return {
|
|
155
|
+
css: {
|
|
156
|
+
canvas: { ...dimensions.css.canvas },
|
|
157
|
+
cell: { ...dimensions.css.cell }
|
|
158
|
+
},
|
|
159
|
+
device: {
|
|
160
|
+
canvas: { ...dimensions.device.canvas },
|
|
161
|
+
cell: { ...dimensions.device.cell },
|
|
162
|
+
char: { ...dimensions.device.char }
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
148
166
|
|
|
149
167
|
constructor(
|
|
150
168
|
options: Partial<ITerminalOptions> = {}
|
|
@@ -418,6 +436,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
418
436
|
this.element.dir = 'ltr'; // xterm.css assumes LTR
|
|
419
437
|
this.element.classList.add('terminal');
|
|
420
438
|
this.element.classList.add('xterm');
|
|
439
|
+
this.element.classList.toggle('allow-transparency', this.options.allowTransparency);
|
|
440
|
+
this._register(this.optionsService.onSpecificOptionChange('allowTransparency', value => this.element!.classList.toggle('allow-transparency', value)));
|
|
421
441
|
parent.appendChild(this.element);
|
|
422
442
|
|
|
423
443
|
// Performance: Use a document fragment to build the terminal
|
|
@@ -478,6 +498,17 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
478
498
|
this._renderService = this._register(this._instantiationService.createInstance(RenderService, this.rows, this.screenElement));
|
|
479
499
|
this._instantiationService.setService(IRenderService, this._renderService);
|
|
480
500
|
this._register(this._renderService.onRenderedViewportChange(e => this._onRender.fire(e)));
|
|
501
|
+
this._register(this._renderService.onDimensionsChange(e => this._onDimensionsChange.fire({
|
|
502
|
+
css: {
|
|
503
|
+
canvas: { ...e.css.canvas },
|
|
504
|
+
cell: { ...e.css.cell }
|
|
505
|
+
},
|
|
506
|
+
device: {
|
|
507
|
+
canvas: { ...e.device.canvas },
|
|
508
|
+
cell: { ...e.device.cell },
|
|
509
|
+
char: { ...e.device.char }
|
|
510
|
+
}
|
|
511
|
+
})));
|
|
481
512
|
this.onResize(e => this._renderService!.resize(e.cols, e.rows));
|
|
482
513
|
|
|
483
514
|
this._compositionView = this._document.createElement('div');
|
|
@@ -605,8 +636,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
605
636
|
|
|
606
637
|
// send event to CoreMouseService
|
|
607
638
|
function sendEvent(ev: MouseEvent | WheelEvent): boolean {
|
|
608
|
-
//
|
|
609
|
-
const pos = self._mouseService
|
|
639
|
+
// Get mouse coordinates
|
|
640
|
+
const pos = self._mouseService?.getMouseReportCoords(ev, self.screenElement!);
|
|
610
641
|
if (!pos) {
|
|
611
642
|
return false;
|
|
612
643
|
}
|
|
@@ -773,6 +804,16 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
773
804
|
// force initial onProtocolChange so we dont miss early mouse requests
|
|
774
805
|
this.coreMouseService.activeProtocol = this.coreMouseService.activeProtocol;
|
|
775
806
|
|
|
807
|
+
// Ensure document-level listeners are removed on dispose
|
|
808
|
+
this._register(toDisposable(() => {
|
|
809
|
+
if (requestedEvents.mouseup) {
|
|
810
|
+
this._document!.removeEventListener('mouseup', requestedEvents.mouseup);
|
|
811
|
+
}
|
|
812
|
+
if (requestedEvents.mousedrag) {
|
|
813
|
+
this._document!.removeEventListener('mousemove', requestedEvents.mousedrag);
|
|
814
|
+
}
|
|
815
|
+
}));
|
|
816
|
+
|
|
776
817
|
/**
|
|
777
818
|
* "Always on" event listeners.
|
|
778
819
|
*/
|
|
@@ -849,8 +890,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
849
890
|
* @param start The row to start from (between 0 and this.rows - 1).
|
|
850
891
|
* @param end The row to end at (between start and this.rows - 1).
|
|
851
892
|
*/
|
|
852
|
-
public refresh(start: number, end: number): void {
|
|
853
|
-
this._renderService?.refreshRows(start, end);
|
|
893
|
+
public refresh(start: number, end: number, sync: boolean = false): void {
|
|
894
|
+
this._renderService?.refreshRows(start, end, sync);
|
|
854
895
|
}
|
|
855
896
|
|
|
856
897
|
/**
|
|
@@ -1283,7 +1324,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
1283
1324
|
this._customKeyEventHandler = customKeyEventHandler;
|
|
1284
1325
|
|
|
1285
1326
|
// do a full screen refresh
|
|
1286
|
-
this.refresh(0, this.rows - 1);
|
|
1327
|
+
this.refresh(0, this.rows - 1, true);
|
|
1287
1328
|
}
|
|
1288
1329
|
|
|
1289
1330
|
public clearTextureAtlas(): void {
|
package/src/browser/Types.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { CharData, IColor, ICoreTerminal, ITerminalOptions } from 'common/Types';
|
|
7
7
|
import { IBuffer } from 'common/buffer/Types';
|
|
8
|
-
import { IDisposable, Terminal as ITerminalApi } from '@xterm/xterm';
|
|
8
|
+
import { IDisposable, IRenderDimensions as IRenderDimensionsApi, Terminal as ITerminalApi } from '@xterm/xterm';
|
|
9
9
|
import { channels, css } from 'common/Color';
|
|
10
10
|
import type { Event } from 'vs/base/common/event';
|
|
11
11
|
|
|
@@ -21,8 +21,11 @@ export interface ITerminal extends InternalPassthroughApis, ICoreTerminal {
|
|
|
21
21
|
linkifier: ILinkifier2 | undefined;
|
|
22
22
|
options: Required<ITerminalOptions>;
|
|
23
23
|
|
|
24
|
+
readonly dimensions: IRenderDimensionsApi | undefined;
|
|
25
|
+
|
|
24
26
|
onBlur: Event<void>;
|
|
25
27
|
onFocus: Event<void>;
|
|
28
|
+
onDimensionsChange: Event<IRenderDimensionsApi>;
|
|
26
29
|
onA11yChar: Event<string>;
|
|
27
30
|
onA11yTab: Event<number>;
|
|
28
31
|
onWillOpen: Event<HTMLElement>;
|
package/src/browser/Viewport.ts
CHANGED
|
@@ -8,11 +8,12 @@ import { ViewportConstants } from 'browser/shared/Constants';
|
|
|
8
8
|
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
|
9
9
|
import { IBufferService, ICoreMouseService, IOptionsService } from 'common/services/Services';
|
|
10
10
|
import { CoreMouseEventType } from 'common/Types';
|
|
11
|
-
import { scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
|
11
|
+
import { addDisposableListener, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
|
12
12
|
import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
|
13
13
|
import type { ScrollableElementChangeOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
|
14
14
|
import { Emitter, Event } from 'vs/base/common/event';
|
|
15
15
|
import { Scrollable, ScrollbarVisibility, type ScrollEvent } from 'vs/base/common/scrollable';
|
|
16
|
+
import { Gesture, EventType as GestureEventType, type GestureEvent } from 'vs/base/browser/touch';
|
|
16
17
|
|
|
17
18
|
export class Viewport extends Disposable {
|
|
18
19
|
|
|
@@ -71,6 +72,7 @@ export class Viewport extends Disposable {
|
|
|
71
72
|
|
|
72
73
|
this._scrollableElement.setScrollDimensions({ height: 0, scrollHeight: 0 });
|
|
73
74
|
this._register(Event.runAndSubscribe(themeService.onChangeColors, () => {
|
|
75
|
+
element.style.backgroundColor = themeService.colors.background.css;
|
|
74
76
|
this._scrollableElement.getDomNode().style.backgroundColor = themeService.colors.background.css;
|
|
75
77
|
}));
|
|
76
78
|
element.appendChild(this._scrollableElement.getDomNode());
|
|
@@ -103,6 +105,10 @@ export class Viewport extends Disposable {
|
|
|
103
105
|
this._register(this._bufferService.onScroll(() => this._sync()));
|
|
104
106
|
|
|
105
107
|
this._register(this._scrollableElement.onScroll(e => this._handleScroll(e)));
|
|
108
|
+
|
|
109
|
+
// Touch/gesture scrolling support
|
|
110
|
+
this._register(Gesture.addTarget(screenElement));
|
|
111
|
+
this._register(addDisposableListener(screenElement, GestureEventType.Change, (e: GestureEvent) => this._handleGestureChange(e)));
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
public scrollLines(disp: number): void {
|
|
@@ -189,4 +195,13 @@ export class Viewport extends Disposable {
|
|
|
189
195
|
}
|
|
190
196
|
this._isHandlingScroll = false;
|
|
191
197
|
}
|
|
198
|
+
|
|
199
|
+
private _handleGestureChange(e: GestureEvent): void {
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
e.stopPropagation();
|
|
202
|
+
const pos = this._scrollableElement.getScrollPosition();
|
|
203
|
+
this._scrollableElement.setScrollPosition({
|
|
204
|
+
scrollTop: pos.scrollTop - e.translationY
|
|
205
|
+
});
|
|
206
|
+
}
|
|
192
207
|
}
|
|
@@ -41,6 +41,11 @@ export class CompositionHelper {
|
|
|
41
41
|
*/
|
|
42
42
|
private _dataAlreadySent: string;
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* The pending textarea change timer, if any.
|
|
46
|
+
*/
|
|
47
|
+
private _textareaChangeTimer?: number;
|
|
48
|
+
|
|
44
49
|
constructor(
|
|
45
50
|
private readonly _textarea: HTMLTextAreaElement,
|
|
46
51
|
private readonly _compositionView: HTMLElement,
|
|
@@ -184,8 +189,12 @@ export class CompositionHelper {
|
|
|
184
189
|
* IME is active.
|
|
185
190
|
*/
|
|
186
191
|
private _handleAnyTextareaChanges(): void {
|
|
192
|
+
if (this._textareaChangeTimer) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
187
195
|
const oldValue = this._textarea.value;
|
|
188
|
-
setTimeout(() => {
|
|
196
|
+
this._textareaChangeTimer = window.setTimeout(() => {
|
|
197
|
+
this._textareaChangeTimer = undefined;
|
|
189
198
|
// Ignore if a composition has started since the timeout
|
|
190
199
|
if (!this._isComposing) {
|
|
191
200
|
const newValue = this._textarea.value;
|
|
@@ -12,7 +12,7 @@ import { AddonManager } from 'common/public/AddonManager';
|
|
|
12
12
|
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
|
|
13
13
|
import { ParserApi } from 'common/public/ParserApi';
|
|
14
14
|
import { UnicodeApi } from 'common/public/UnicodeApi';
|
|
15
|
-
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
|
|
15
|
+
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, IRenderDimensions, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
|
|
16
16
|
import type { Event } from 'vs/base/common/event';
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -80,6 +80,7 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
80
80
|
public get onSelectionChange(): Event<void> { return this._core.onSelectionChange; }
|
|
81
81
|
public get onTitleChange(): Event<string> { return this._core.onTitleChange; }
|
|
82
82
|
public get onWriteParsed(): Event<void> { return this._core.onWriteParsed; }
|
|
83
|
+
public get onDimensionsChange(): Event<IRenderDimensions> { return this._core.onDimensionsChange; }
|
|
83
84
|
|
|
84
85
|
public get element(): HTMLElement | undefined { return this._core.element; }
|
|
85
86
|
public get parser(): IParser {
|
|
@@ -102,7 +103,6 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
102
103
|
return this._buffer;
|
|
103
104
|
}
|
|
104
105
|
public get markers(): ReadonlyArray<IMarker> {
|
|
105
|
-
this._checkProposedApi();
|
|
106
106
|
return this._core.markers;
|
|
107
107
|
}
|
|
108
108
|
public get modes(): IModes {
|
|
@@ -123,10 +123,14 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
123
123
|
originMode: m.origin,
|
|
124
124
|
reverseWraparoundMode: m.reverseWraparound,
|
|
125
125
|
sendFocusMode: m.sendFocus,
|
|
126
|
+
showCursor: !this._core.coreService.isCursorHidden,
|
|
126
127
|
synchronizedOutputMode: m.synchronizedOutput,
|
|
127
128
|
wraparoundMode: m.wraparound
|
|
128
129
|
};
|
|
129
130
|
}
|
|
131
|
+
public get dimensions(): IRenderDimensions | undefined {
|
|
132
|
+
return this._core.dimensions;
|
|
133
|
+
}
|
|
130
134
|
public get options(): Required<ITerminalOptions> {
|
|
131
135
|
return this._publicOptions;
|
|
132
136
|
}
|
|
@@ -161,11 +165,9 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
161
165
|
return this._core.registerLinkProvider(linkProvider);
|
|
162
166
|
}
|
|
163
167
|
public registerCharacterJoiner(handler: (text: string) => [number, number][]): number {
|
|
164
|
-
this._checkProposedApi();
|
|
165
168
|
return this._core.registerCharacterJoiner(handler);
|
|
166
169
|
}
|
|
167
170
|
public deregisterCharacterJoiner(joinerId: number): void {
|
|
168
|
-
this._checkProposedApi();
|
|
169
171
|
this._core.deregisterCharacterJoiner(joinerId);
|
|
170
172
|
}
|
|
171
173
|
public registerMarker(cursorYOffset: number = 0): IMarker {
|
|
@@ -173,7 +175,6 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
173
175
|
return this._core.registerMarker(cursorYOffset);
|
|
174
176
|
}
|
|
175
177
|
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
|
|
176
|
-
this._checkProposedApi();
|
|
177
178
|
this._verifyPositiveIntegers(decorationOptions.x ?? 0, decorationOptions.width ?? 0, decorationOptions.height ?? 0);
|
|
178
179
|
return this._core.registerDecoration(decorationOptions);
|
|
179
180
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
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
11
|
import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
|
@@ -15,6 +15,7 @@ import { color } from 'common/Color';
|
|
|
15
15
|
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
|
16
16
|
import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
|
17
17
|
import { Emitter } from 'vs/base/common/event';
|
|
18
|
+
import { addDisposableListener } from 'vs/base/browser/dom';
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
|
|
@@ -23,6 +24,7 @@ const FG_CLASS_PREFIX = 'xterm-fg-';
|
|
|
23
24
|
const BG_CLASS_PREFIX = 'xterm-bg-';
|
|
24
25
|
const FOCUS_CLASS = 'xterm-focus';
|
|
25
26
|
const SELECTION_CLASS = 'xterm-selection';
|
|
27
|
+
const CURSOR_BLINK_IDLE_CLASS = 'xterm-cursor-blink-idle';
|
|
26
28
|
|
|
27
29
|
let nextTerminalId = 1;
|
|
28
30
|
|
|
@@ -42,6 +44,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
42
44
|
private _selectionContainer: HTMLElement;
|
|
43
45
|
private _widthCache: WidthCache;
|
|
44
46
|
private _selectionRenderModel: ISelectionRenderModel = createSelectionRenderModel();
|
|
47
|
+
private _cursorBlinkStateManager: CursorBlinkStateManager;
|
|
45
48
|
|
|
46
49
|
public dimensions: IRenderDimensions;
|
|
47
50
|
|
|
@@ -89,6 +92,10 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
89
92
|
this._register(this._linkifier2.onShowLinkUnderline(e => this._handleLinkHover(e)));
|
|
90
93
|
this._register(this._linkifier2.onHideLinkUnderline(e => this._handleLinkLeave(e)));
|
|
91
94
|
|
|
95
|
+
this._cursorBlinkStateManager = new CursorBlinkStateManager(this._rowContainer, this._coreBrowserService);
|
|
96
|
+
this._register(addDisposableListener(this._document, 'mousedown', () => this._cursorBlinkStateManager.restartBlinkAnimation()));
|
|
97
|
+
this._register(toDisposable(() => this._cursorBlinkStateManager.dispose()));
|
|
98
|
+
|
|
92
99
|
this._register(toDisposable(() => {
|
|
93
100
|
this._element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
|
94
101
|
|
|
@@ -101,7 +108,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
101
108
|
this._dimensionsStyleElement.remove();
|
|
102
109
|
}));
|
|
103
110
|
|
|
104
|
-
this._widthCache = new WidthCache(
|
|
111
|
+
this._widthCache = new WidthCache();
|
|
105
112
|
this._widthCache.setFont(
|
|
106
113
|
this._optionsService.rawOptions.fontFamily,
|
|
107
114
|
this._optionsService.rawOptions.fontSize,
|
|
@@ -225,6 +232,10 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
225
232
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
|
226
233
|
` animation: ${blinkAnimationBlockId} 1s step-end infinite;` +
|
|
227
234
|
`}` +
|
|
235
|
+
// Disable cursor blinking when idle
|
|
236
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` +
|
|
237
|
+
` animation: none !important;` +
|
|
238
|
+
`}` +
|
|
228
239
|
// !important helps fix an issue where the cursor will not render on top of the selection,
|
|
229
240
|
// however it's very hard to fix this issue and retain the blink animation without the use of
|
|
230
241
|
// !important. So this edge case fails when cursor blink is on.
|
|
@@ -328,11 +339,13 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
328
339
|
|
|
329
340
|
public handleBlur(): void {
|
|
330
341
|
this._rowContainer.classList.remove(FOCUS_CLASS);
|
|
342
|
+
this._cursorBlinkStateManager.pause();
|
|
331
343
|
this.renderRows(0, this._bufferService.rows - 1);
|
|
332
344
|
}
|
|
333
345
|
|
|
334
346
|
public handleFocus(): void {
|
|
335
347
|
this._rowContainer.classList.add(FOCUS_CLASS);
|
|
348
|
+
this._cursorBlinkStateManager.resume();
|
|
336
349
|
this.renderRows(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
|
337
350
|
}
|
|
338
351
|
|
|
@@ -406,7 +419,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
406
419
|
}
|
|
407
420
|
|
|
408
421
|
public handleCursorMove(): void {
|
|
409
|
-
//
|
|
422
|
+
// Reset idle timer on cursor movement (which happens on input)
|
|
423
|
+
this._cursorBlinkStateManager.restartBlinkAnimation();
|
|
410
424
|
}
|
|
411
425
|
|
|
412
426
|
private _handleOptionsChanged(): void {
|
|
@@ -540,3 +554,60 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
540
554
|
}
|
|
541
555
|
}
|
|
542
556
|
}
|
|
557
|
+
|
|
558
|
+
class CursorBlinkStateManager {
|
|
559
|
+
private _idleTimeout: number | undefined;
|
|
560
|
+
private _isIdlePaused: boolean = false;
|
|
561
|
+
|
|
562
|
+
constructor(
|
|
563
|
+
private readonly _rowContainer: HTMLElement,
|
|
564
|
+
private readonly _coreBrowserService: ICoreBrowserService
|
|
565
|
+
) {
|
|
566
|
+
if (this._coreBrowserService.isFocused) {
|
|
567
|
+
this._resetIdleTimer();
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
public dispose(): void {
|
|
572
|
+
this._clearIdleTimer();
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
public restartBlinkAnimation(): void {
|
|
576
|
+
if (this._isIdlePaused) {
|
|
577
|
+
this._rowContainer.classList.remove(CURSOR_BLINK_IDLE_CLASS);
|
|
578
|
+
}
|
|
579
|
+
this._resetIdleTimer();
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
public pause(): void {
|
|
583
|
+
this._isIdlePaused = false;
|
|
584
|
+
this._clearIdleTimer();
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
public resume(): void {
|
|
588
|
+
this._isIdlePaused = false;
|
|
589
|
+
this._rowContainer.classList.remove(CURSOR_BLINK_IDLE_CLASS);
|
|
590
|
+
this._resetIdleTimer();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private _resetIdleTimer(): void {
|
|
594
|
+
this._isIdlePaused = false;
|
|
595
|
+
this._clearIdleTimer();
|
|
596
|
+
this._idleTimeout = this._coreBrowserService.window.setTimeout(() => {
|
|
597
|
+
this._stopBlinkingDueToIdle();
|
|
598
|
+
}, RendererConstants.CURSOR_BLINK_IDLE_TIMEOUT);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
private _clearIdleTimer(): void {
|
|
602
|
+
if (this._idleTimeout) {
|
|
603
|
+
this._coreBrowserService.window.clearTimeout(this._idleTimeout);
|
|
604
|
+
this._idleTimeout = undefined;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
private _stopBlinkingDueToIdle(): void {
|
|
609
|
+
this._rowContainer.classList.add(CURSOR_BLINK_IDLE_CLASS);
|
|
610
|
+
this._isIdlePaused = true;
|
|
611
|
+
this._idleTimeout = undefined;
|
|
612
|
+
}
|
|
613
|
+
}
|