@xterm/xterm 5.6.0-beta.4 → 5.6.0-beta.41

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/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@xterm/xterm",
3
3
  "description": "Full xterm terminal, in your browser",
4
- "version": "5.6.0-beta.4",
4
+ "version": "5.6.0-beta.41",
5
5
  "main": "lib/xterm.js",
6
+ "module": "lib/xterm.mjs",
6
7
  "style": "css/xterm.css",
7
8
  "types": "typings/xterm.d.ts",
8
9
  "repository": "https://github.com/xtermjs/xterm.js",
@@ -23,44 +24,51 @@
23
24
  "xterm"
24
25
  ],
25
26
  "scripts": {
26
- "prepackage": "npm run build",
27
- "package": "webpack",
28
- "package-headless": "webpack --config ./webpack.config.headless.js",
29
- "postpackage-headless": "node ./bin/package_headless.js",
27
+ "setup": "npm run build",
28
+ "presetup": "npm run install-addons",
29
+ "install-addons": "node ./bin/install-addons.js",
30
30
  "start": "node demo/start",
31
- "start-server-only": "node demo/start-server-only",
32
31
  "build-demo": "webpack --config ./demo/webpack.config.js",
33
- "lint": "eslint -c .eslintrc.json --max-warnings 0 --ext .ts src/ addons/",
34
- "lint-api": "eslint --no-eslintrc -c .eslintrc.json.typings --max-warnings 0 --no-ignore --ext .d.ts typings/",
32
+ "build": "npm run tsc",
33
+ "watch": "npm run tsc-watch",
34
+ "tsc": "tsc -b ./tsconfig.all.json",
35
+ "tsc-watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput",
36
+ "esbuild": "node bin/esbuild_all.mjs",
37
+ "esbuild-watch": "node bin/esbuild_all.mjs --watch",
38
+ "esbuild-package": "node bin/esbuild_all.mjs --prod",
39
+ "esbuild-package-watch": "node bin/esbuild_all.mjs --prod --watch",
40
+ "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",
35
43
  "test": "npm run test-unit",
36
44
  "posttest": "npm run lint",
37
- "test-api": "npm run test-api-chromium",
38
- "test-api-chromium": "node ./bin/test_api.js --browser=chromium --timeout=20000",
39
- "test-api-firefox": "node ./bin/test_api.js --browser=firefox --timeout=20000",
40
- "test-api-webkit": "node ./bin/test_api.js --browser=webkit --timeout=20000",
41
- "test-playwright": "node ./bin/test_playwright.js --workers=75%",
42
- "test-playwright-chromium": "node ./bin/test_playwright.js --workers=75% \"--project=Chrome Stable\"",
43
- "test-playwright-firefox": "node ./bin/test_playwright.js --workers=75% \"--project=Firefox Stable\"",
44
- "test-playwright-webkit": "node ./bin/test_playwright.js --workers=75% \"--project=WebKit\"",
45
- "test-playwright-debug": "node ./bin/test_playwright.js --workers=1 --headed --timeout=30000",
46
- "test-unit": "node ./bin/test.js",
47
- "test-unit-coverage": "node ./bin/test.js --coverage",
45
+ "lint": "eslint -c .eslintrc.json --max-warnings 0 --ext .ts src/ addons/",
46
+ "lint-api": "eslint --no-eslintrc -c .eslintrc.json.typings --max-warnings 0 --no-ignore --ext .d.ts typings/",
47
+ "test-unit": "node ./bin/test_unit.js",
48
+ "test-unit-coverage": "node ./bin/test_unit.js --coverage",
48
49
  "test-unit-dev": "cross-env NODE_PATH='./out' mocha",
49
- "build": "tsc -b ./tsconfig.all.json",
50
- "install-addons": "node ./bin/install-addons.js",
51
- "presetup": "npm run install-addons",
52
- "setup": "npm run build",
53
- "prepublishOnly": "npm run package",
54
- "watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput",
50
+ "test-integration": "node ./bin/test_integration.js --workers=75%",
51
+ "test-integration-chromium": "node ./bin/test_integration.js --workers=75% \"--project=ChromeStable\"",
52
+ "test-integration-firefox": "node ./bin/test_integration.js --workers=75% \"--project=FirefoxStable\"",
53
+ "test-integration-webkit": "node ./bin/test_integration.js --workers=75% \"--project=WebKit\"",
54
+ "test-integration-debug": "node ./bin/test_integration.js --workers=1 --headed --timeout=30000",
55
55
  "benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json",
56
- "benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/test/benchmark/*benchmark.js",
57
- "benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/test/benchmark/*benchmark.js",
56
+ "benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-tsc/test-benchmark/test/benchmark/*benchmark.js",
57
+ "benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-tsc/test-benchmark/test/benchmark/*benchmark.js",
58
58
  "clean": "rm -rf lib out addons/*/lib addons/*/out",
59
- "vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts"
59
+ "vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts",
60
+ "prepackage": "npm run build",
61
+ "package": "webpack",
62
+ "postpackage": "npm run esbuild-package",
63
+ "prepackage-headless": "npm run esbuild-package-headless-only",
64
+ "package-headless": "webpack --config ./webpack.config.headless.js",
65
+ "postpackage-headless": "node ./bin/package_headless.js",
66
+ "prepublishOnly": "npm run package"
60
67
  },
61
68
  "devDependencies": {
62
69
  "@lunapaint/png-codec": "^0.2.0",
63
70
  "@playwright/test": "^1.37.1",
71
+ "@stylistic/eslint-plugin": "^2.3.0",
64
72
  "@types/chai": "^4.2.22",
65
73
  "@types/debug": "^4.1.7",
66
74
  "@types/deep-equal": "^1.0.1",
@@ -78,9 +86,10 @@
78
86
  "chai": "^4.3.4",
79
87
  "cross-env": "^7.0.3",
80
88
  "deep-equal": "^2.0.5",
89
+ "esbuild": "^0.23.0",
81
90
  "eslint": "^8.56.0",
82
91
  "eslint-plugin-jsdoc": "^46.9.1",
83
- "express": "^4.17.1",
92
+ "express": "^4.19.2",
84
93
  "express-ws": "^5.0.2",
85
94
  "glob": "^7.2.0",
86
95
  "jsdom": "^18.0.1",
@@ -91,7 +100,7 @@
91
100
  "source-map-loader": "^3.0.0",
92
101
  "source-map-support": "^0.5.20",
93
102
  "ts-loader": "^9.3.1",
94
- "typescript": "^5.1.6",
103
+ "typescript": "5.5",
95
104
  "utf8": "^3.0.0",
96
105
  "webpack": "^5.61.0",
97
106
  "webpack-cli": "^4.9.1",
@@ -58,10 +58,11 @@ export class AccessibilityManager extends Disposable {
58
58
  @IRenderService private readonly _renderService: IRenderService
59
59
  ) {
60
60
  super();
61
- this._accessibilityContainer = this._coreBrowserService.mainDocument.createElement('div');
61
+ const doc = this._coreBrowserService.mainDocument;
62
+ this._accessibilityContainer = doc.createElement('div');
62
63
  this._accessibilityContainer.classList.add('xterm-accessibility');
63
64
 
64
- this._rowContainer = this._coreBrowserService.mainDocument.createElement('div');
65
+ this._rowContainer = doc.createElement('div');
65
66
  this._rowContainer.setAttribute('role', 'list');
66
67
  this._rowContainer.classList.add('xterm-accessibility-tree');
67
68
  this._rowElements = [];
@@ -75,10 +76,9 @@ export class AccessibilityManager extends Disposable {
75
76
  this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);
76
77
  this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
77
78
 
78
- this._refreshRowsDimensions();
79
79
  this._accessibilityContainer.appendChild(this._rowContainer);
80
80
 
81
- this._liveRegion = this._coreBrowserService.mainDocument.createElement('div');
81
+ this._liveRegion = doc.createElement('div');
82
82
  this._liveRegion.classList.add('live-region');
83
83
  this._liveRegion.setAttribute('aria-live', 'assertive');
84
84
  this._accessibilityContainer.appendChild(this._liveRegion);
@@ -93,12 +93,12 @@ export class AccessibilityManager extends Disposable {
93
93
  this._rowContainer.classList.add('debug');
94
94
 
95
95
  // Use a `<div class="xterm">` container so that the css will still apply.
96
- this._debugRootContainer = document.createElement('div');
96
+ this._debugRootContainer = doc.createElement('div');
97
97
  this._debugRootContainer.classList.add('xterm');
98
98
 
99
- this._debugRootContainer.appendChild(document.createTextNode('------start a11y------'));
99
+ this._debugRootContainer.appendChild(doc.createTextNode('------start a11y------'));
100
100
  this._debugRootContainer.appendChild(this._accessibilityContainer);
101
- this._debugRootContainer.appendChild(document.createTextNode('------end a11y------'));
101
+ this._debugRootContainer.appendChild(doc.createTextNode('------end a11y------'));
102
102
 
103
103
  this._terminal.element.insertAdjacentElement('afterend', this._debugRootContainer);
104
104
  } else {
@@ -115,9 +115,10 @@ export class AccessibilityManager extends Disposable {
115
115
  this.register(this._terminal.onKey(e => this._handleKey(e.key)));
116
116
  this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
117
117
  this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
118
- this.register(addDisposableDomListener(document, 'selectionchange', () => this._handleSelectionChange()));
118
+ this.register(addDisposableDomListener(doc, 'selectionchange', () => this._handleSelectionChange()));
119
119
  this.register(this._coreBrowserService.onDprChange(() => this._refreshRowsDimensions()));
120
120
 
121
+ this._refreshRowsDimensions();
121
122
  this._refreshRows();
122
123
  this.register(toDisposable(() => {
123
124
  if (DEBUG) {
@@ -150,7 +151,7 @@ export class AccessibilityManager extends Disposable {
150
151
  if (char === '\n') {
151
152
  this._liveRegionLineCount++;
152
153
  if (this._liveRegionLineCount === MAX_ROWS_TO_READ + 1) {
153
- this._liveRegion.textContent += Strings.tooMuchOutput;
154
+ this._liveRegion.textContent += Strings.tooMuchOutput.get();
154
155
  }
155
156
  }
156
157
  }
@@ -192,6 +193,7 @@ export class AccessibilityManager extends Disposable {
192
193
  }
193
194
  element.setAttribute('aria-posinset', posInSet);
194
195
  element.setAttribute('aria-setsize', setSize);
196
+ this._alignRowWidth(element);
195
197
  }
196
198
  }
197
199
  this._announceCharacters();
@@ -270,7 +272,7 @@ export class AccessibilityManager extends Disposable {
270
272
  return;
271
273
  }
272
274
 
273
- const selection = document.getSelection();
275
+ const selection = this._coreBrowserService.mainDocument.getSelection();
274
276
  if (!selection) {
275
277
  return;
276
278
  }
@@ -389,19 +391,45 @@ export class AccessibilityManager extends Disposable {
389
391
  this._refreshRowDimensions(element);
390
392
  return element;
391
393
  }
394
+
392
395
  private _refreshRowsDimensions(): void {
393
396
  if (!this._renderService.dimensions.css.cell.height) {
394
397
  return;
395
398
  }
396
- this._accessibilityContainer.style.width = `${this._renderService.dimensions.css.canvas.width}px`;
399
+ Object.assign(this._accessibilityContainer.style, {
400
+ width: `${this._renderService.dimensions.css.canvas.width}px`,
401
+ fontSize: `${this._terminal.options.fontSize}px`
402
+ });
397
403
  if (this._rowElements.length !== this._terminal.rows) {
398
404
  this._handleResize(this._terminal.rows);
399
405
  }
400
406
  for (let i = 0; i < this._terminal.rows; i++) {
401
407
  this._refreshRowDimensions(this._rowElements[i]);
408
+ this._alignRowWidth(this._rowElements[i]);
402
409
  }
403
410
  }
411
+
404
412
  private _refreshRowDimensions(element: HTMLElement): void {
405
413
  element.style.height = `${this._renderService.dimensions.css.cell.height}px`;
406
414
  }
415
+
416
+ /**
417
+ * Scale the width of a row so that each of the character is (mostly) aligned
418
+ * with the actual rendering. This will allow the screen reader to draw
419
+ * selection outline at the correct position.
420
+ *
421
+ * On top of using the "monospace" font and correct font size, the scaling
422
+ * here is necessary to handle characters that are not covered by the font
423
+ * (e.g. CJK).
424
+ */
425
+ private _alignRowWidth(element: HTMLElement): void {
426
+ element.style.transform = '';
427
+ const width = element.getBoundingClientRect().width;
428
+ const lastColumn = this._rowColumns.get(element)?.slice(-1)?.[0];
429
+ if (!lastColumn) {
430
+ return;
431
+ }
432
+ const targetWidth = lastColumn * this._renderService.dimensions.css.cell.width;
433
+ element.style.transform = `scaleX(${targetWidth / width})`;
434
+ }
407
435
  }
@@ -59,7 +59,7 @@ import { WindowsOptionsReportType } from '../common/InputHandler';
59
59
  import { AccessibilityManager } from './AccessibilityManager';
60
60
  import { LinkProviderService } from 'browser/services/LinkProviderService';
61
61
 
62
- export class Terminal extends CoreTerminal implements ITerminal {
62
+ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
63
63
  public textarea: HTMLTextAreaElement | undefined;
64
64
  public element: HTMLElement | undefined;
65
65
  public screenElement: HTMLElement | undefined;
@@ -443,7 +443,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
443
443
 
444
444
  this.textarea = this._document.createElement('textarea');
445
445
  this.textarea.classList.add('xterm-helper-textarea');
446
- this.textarea.setAttribute('aria-label', Strings.promptLabel);
446
+ this.textarea.setAttribute('aria-label', Strings.promptLabel.get());
447
447
  if (!Browser.isChromeOS) {
448
448
  // ChromeVox on ChromeOS does not like this. See
449
449
  // https://issuetracker.google.com/issues/260170397
@@ -5,8 +5,19 @@
5
5
 
6
6
  // This file contains strings that get exported in the API so they can be localized
7
7
 
8
- // eslint-disable-next-line prefer-const
9
- export let promptLabel = 'Terminal input';
8
+ let promptLabelInternal = 'Terminal input';
9
+ const promptLabel = {
10
+ get: () => promptLabelInternal,
11
+ set: (value: string) => promptLabelInternal = value
12
+ };
10
13
 
11
- // eslint-disable-next-line prefer-const
12
- export let tooMuchOutput = 'Too much output to announce, navigate to rows manually to read';
14
+ let tooMuchOutputInternal = 'Too much output to announce, navigate to rows manually to read';
15
+ const tooMuchOutput = {
16
+ get: () => tooMuchOutputInternal,
17
+ set: (value: string) => tooMuchOutputInternal = value
18
+ };
19
+
20
+ export {
21
+ promptLabel,
22
+ tooMuchOutput
23
+ };
@@ -7,6 +7,7 @@ import { IEvent } from 'common/EventEmitter';
7
7
  import { CharData, IColor, ICoreTerminal, ITerminalOptions } from 'common/Types';
8
8
  import { IBuffer } from 'common/buffer/Types';
9
9
  import { IDisposable, Terminal as ITerminalApi } from '@xterm/xterm';
10
+ import { channels, css } from 'common/Color';
10
11
 
11
12
  /**
12
13
  * A portion of the public API that are implemented identially internally and simply passed through.
@@ -129,7 +130,7 @@ export interface ILinkifier2 extends IDisposable {
129
130
  readonly currentLink: ILinkWithState | undefined;
130
131
  }
131
132
 
132
- interface ILink {
133
+ export interface ILink {
133
134
  range: IBufferRange;
134
135
  text: string;
135
136
  decorations?: ILinkDecorations;
@@ -139,17 +140,17 @@ interface ILink {
139
140
  dispose?(): void;
140
141
  }
141
142
 
142
- interface ILinkDecorations {
143
+ export interface ILinkDecorations {
143
144
  pointerCursor: boolean;
144
145
  underline: boolean;
145
146
  }
146
147
 
147
- interface IBufferRange {
148
+ export interface IBufferRange {
148
149
  start: IBufferCellPosition;
149
150
  end: IBufferCellPosition;
150
151
  }
151
152
 
152
- interface IBufferCellPosition {
153
+ export interface IBufferCellPosition {
153
154
  x: number;
154
155
  y: number;
155
156
  }
@@ -172,3 +173,51 @@ export interface IRenderDebouncerWithCallback extends IRenderDebouncer {
172
173
  export interface IBufferElementProvider {
173
174
  provideBufferElements(): DocumentFragment | HTMLElement;
174
175
  }
176
+
177
+ // An IIFE to generate DEFAULT_ANSI_COLORS.
178
+ export const DEFAULT_ANSI_COLORS = Object.freeze((() => {
179
+ const colors = [
180
+ // dark:
181
+ css.toColor('#2e3436'),
182
+ css.toColor('#cc0000'),
183
+ css.toColor('#4e9a06'),
184
+ css.toColor('#c4a000'),
185
+ css.toColor('#3465a4'),
186
+ css.toColor('#75507b'),
187
+ css.toColor('#06989a'),
188
+ css.toColor('#d3d7cf'),
189
+ // bright:
190
+ css.toColor('#555753'),
191
+ css.toColor('#ef2929'),
192
+ css.toColor('#8ae234'),
193
+ css.toColor('#fce94f'),
194
+ css.toColor('#729fcf'),
195
+ css.toColor('#ad7fa8'),
196
+ css.toColor('#34e2e2'),
197
+ css.toColor('#eeeeec')
198
+ ];
199
+
200
+ // Fill in the remaining 240 ANSI colors.
201
+ // Generate colors (16-231)
202
+ const v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff];
203
+ for (let i = 0; i < 216; i++) {
204
+ const r = v[(i / 36) % 6 | 0];
205
+ const g = v[(i / 6) % 6 | 0];
206
+ const b = v[i % 6];
207
+ colors.push({
208
+ css: channels.toCss(r, g, b),
209
+ rgba: channels.toRgba(r, g, b)
210
+ });
211
+ }
212
+
213
+ // Generate greys (232-255)
214
+ for (let i = 0; i < 24; i++) {
215
+ const c = 8 + i * 10;
216
+ colors.push({
217
+ css: channels.toCss(c, c, c),
218
+ rgba: channels.toRgba(c, c, c)
219
+ });
220
+ }
221
+
222
+ return colors;
223
+ })());
@@ -51,6 +51,8 @@ export class Viewport extends Disposable implements IViewport {
51
51
  target: -1
52
52
  };
53
53
 
54
+ private _ensureTimeout: number;
55
+
54
56
  private readonly _onRequestScrollLines = this.register(new EventEmitter<{ amount: number, suppressScrollEvent: boolean }>());
55
57
  public readonly onRequestScrollLines = this._onRequestScrollLines.event;
56
58
 
@@ -83,7 +85,7 @@ export class Viewport extends Disposable implements IViewport {
83
85
  this.register(this._optionsService.onSpecificOptionChange('scrollback', () => this.syncScrollArea()));
84
86
 
85
87
  // Perform this async to ensure the ICharSizeService is ready.
86
- setTimeout(() => this.syncScrollArea());
88
+ this._ensureTimeout = window.setTimeout(() => this.syncScrollArea());
87
89
  }
88
90
 
89
91
  private _handleThemeChange(colors: ReadonlyColorSet): void {
@@ -405,4 +407,8 @@ export class Viewport extends Disposable implements IViewport {
405
407
  this._viewportElement.scrollTop += deltaY;
406
408
  return this._bubbleScroll(ev, deltaY);
407
409
  }
410
+
411
+ public dispose(): void {
412
+ clearTimeout(this._ensureTimeout);
413
+ }
408
414
  }
@@ -108,8 +108,13 @@ export class BufferDecorationRenderer extends Disposable {
108
108
  element!.remove();
109
109
  });
110
110
  }
111
- element.style.top = `${line * this._renderService.dimensions.css.cell.height}px`;
112
111
  element.style.display = this._altBufferIsActive ? 'none' : 'block';
112
+ if (!this._altBufferIsActive) {
113
+ element.style.width = `${Math.round((decoration.options.width || 1) * this._renderService.dimensions.css.cell.width)}px`;
114
+ element.style.height = `${(decoration.options.height || 1) * this._renderService.dimensions.css.cell.height}px`;
115
+ element.style.top = `${line * this._renderService.dimensions.css.cell.height}px`;
116
+ element.style.lineHeight = `${this._renderService.dimensions.css.cell.height}px`;
117
+ }
113
118
  decoration.onRenderEmitter.fire(element);
114
119
  }
115
120
  }
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import * as Strings from 'browser/LocalizableStrings';
7
- import { Terminal as TerminalCore } from 'browser/Terminal';
7
+ import { CoreBrowserTerminal as TerminalCore } from 'browser/CoreBrowserTerminal';
8
8
  import { IBufferRange, ITerminal } from 'browser/Types';
9
9
  import { IEvent } from 'common/EventEmitter';
10
10
  import { Disposable } from 'common/Lifecycle';
@@ -20,6 +20,8 @@ import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOption
20
20
  */
21
21
  const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows'];
22
22
 
23
+ let $value = 0;
24
+
23
25
  export class Terminal extends Disposable implements ITerminalApi {
24
26
  private _core: ITerminal;
25
27
  private _addonManager: AddonManager;
@@ -245,20 +247,26 @@ export class Terminal extends Disposable implements ITerminalApi {
245
247
  this._addonManager.loadAddon(this, addon);
246
248
  }
247
249
  public static get strings(): ILocalizableStrings {
248
- return Strings;
250
+ // A wrapper is required here because esbuild prevents setting an `export let`
251
+ return {
252
+ get promptLabel(): string { return Strings.promptLabel.get(); },
253
+ set promptLabel(value: string) { Strings.promptLabel.set(value); },
254
+ get tooMuchOutput(): string { return Strings.tooMuchOutput.get(); },
255
+ set tooMuchOutput(value: string) { Strings.tooMuchOutput.set(value); }
256
+ };
249
257
  }
250
258
 
251
259
  private _verifyIntegers(...values: number[]): void {
252
- for (const value of values) {
253
- if (value === Infinity || isNaN(value) || value % 1 !== 0) {
260
+ for ($value of values) {
261
+ if ($value === Infinity || isNaN($value) || $value % 1 !== 0) {
254
262
  throw new Error('This API only accepts integers');
255
263
  }
256
264
  }
257
265
  }
258
266
 
259
267
  private _verifyPositiveIntegers(...values: number[]): void {
260
- for (const value of values) {
261
- if (value && (value === Infinity || isNaN(value) || value % 1 !== 0 || value < 0)) {
268
+ for ($value of values) {
269
+ if ($value && ($value === Infinity || isNaN($value) || $value % 1 !== 0 || $value < 0)) {
262
270
  throw new Error('This API only accepts positive integers');
263
271
  }
264
272
  }
@@ -343,6 +343,9 @@ export class DomRenderer extends Disposable implements IRenderer {
343
343
  }
344
344
 
345
345
  this._selectionRenderModel.update(this._terminal, start, end, columnSelectMode);
346
+ if (!this._selectionRenderModel.hasSelection) {
347
+ return;
348
+ }
346
349
 
347
350
  // Translate from buffer position to viewport position
348
351
  const viewportStartRow = this._selectionRenderModel.viewportStartRow;
@@ -350,11 +353,6 @@ export class DomRenderer extends Disposable implements IRenderer {
350
353
  const viewportCappedStartRow = this._selectionRenderModel.viewportCappedStartRow;
351
354
  const viewportCappedEndRow = this._selectionRenderModel.viewportCappedEndRow;
352
355
 
353
- // No need to draw the selection
354
- if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
355
- return;
356
- }
357
-
358
356
  // Create the selections
359
357
  const documentFragment = this._document.createDocumentFragment();
360
358
 
@@ -355,6 +355,12 @@ const enum VectorType {
355
355
  * Original symbols defined in https://github.com/powerline/fontpatcher
356
356
  */
357
357
  export const powerlineDefinitions: { [index: string]: IVectorShape } = {
358
+ // Git branch
359
+ '\u{E0A0}': { d: 'M.3,1 L.03,1 L.03,.88 C.03,.82,.06,.78,.11,.73 C.15,.7,.2,.68,.28,.65 L.43,.6 C.49,.58,.53,.56,.56,.53 C.59,.5,.6,.47,.6,.43 L.6,.27 L.4,.27 L.69,.1 L.98,.27 L.78,.27 L.78,.46 C.78,.52,.76,.56,.72,.61 C.68,.66,.63,.67,.56,.7 L.48,.72 C.42,.74,.38,.76,.35,.78 C.32,.8,.31,.84,.31,.88 L.31,1 M.3,.5 L.03,.59 L.03,.09 L.3,.09 L.3,.655', type: VectorType.FILL },
360
+ // L N
361
+ '\u{E0A1}': { d: 'M.7,.4 L.7,.47 L.2,.47 L.2,.03 L.355,.03 L.355,.4 L.705,.4 M.7,.5 L.86,.5 L.86,.95 L.69,.95 L.44,.66 L.46,.86 L.46,.95 L.3,.95 L.3,.49 L.46,.49 L.71,.78 L.69,.565 L.69,.5', type: VectorType.FILL },
362
+ // Lock
363
+ '\u{E0A2}': { d: 'M.25,.94 C.16,.94,.11,.92,.11,.87 L.11,.53 C.11,.48,.15,.455,.23,.45 L.23,.3 C.23,.25,.26,.22,.31,.19 C.36,.16,.43,.15,.51,.15 C.59,.15,.66,.16,.71,.19 C.77,.22,.79,.26,.79,.3 L.79,.45 C.87,.45,.91,.48,.91,.53 L.91,.87 C.91,.92,.86,.94,.77,.94 L.24,.94 M.53,.2 C.49,.2,.45,.21,.42,.23 C.39,.25,.38,.27,.38,.3 L.38,.45 L.68,.45 L.68,.3 C.68,.27,.67,.25,.64,.23 C.61,.21,.58,.2,.53,.2 M.58,.82 L.58,.66 C.63,.65,.65,.63,.65,.6 C.65,.58,.64,.57,.61,.56 C.58,.55,.56,.54,.52,.54 C.48,.54,.46,.55,.43,.56 C.4,.57,.39,.59,.39,.6 C.39,.63,.41,.64,.46,.66 L.46,.82 L.57,.82', type: VectorType.FILL },
358
364
  // Right triangle solid
359
365
  '\u{E0B0}': { d: 'M0,0 L1,.5 L0,1', type: VectorType.FILL, rightPadding: 2 },
360
366
  // Right triangle line
@@ -13,7 +13,7 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic
13
13
 
14
14
  private _isFocused = false;
15
15
  private _cachedIsFocused: boolean | undefined = undefined;
16
- private _screenDprMonitor = new ScreenDprMonitor(this._window);
16
+ private _screenDprMonitor = this.register(new ScreenDprMonitor(this._window));
17
17
 
18
18
  private readonly _onDprChange = this.register(new EventEmitter<number>());
19
19
  public readonly onDprChange = this._onDprChange.event;
@@ -31,8 +31,12 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic
31
31
  this.register(this.onWindowChange(w => this._screenDprMonitor.setWindow(w)));
32
32
  this.register(forwardEvent(this._screenDprMonitor.onDprChange, this._onDprChange));
33
33
 
34
- this._textarea.addEventListener('focus', () => this._isFocused = true);
35
- this._textarea.addEventListener('blur', () => this._isFocused = false);
34
+ this.register(
35
+ addDisposableDomListener(this._textarea, 'focus', () => (this._isFocused = true))
36
+ );
37
+ this.register(
38
+ addDisposableDomListener(this._textarea, 'blur', () => (this._isFocused = false))
39
+ );
36
40
  }
37
41
 
38
42
  public get window(): Window & typeof globalThis {
@@ -5,8 +5,8 @@
5
5
 
6
6
  import { ColorContrastCache } from 'browser/ColorContrastCache';
7
7
  import { IThemeService } from 'browser/services/Services';
8
- import { IColorContrastCache, IColorSet, ReadonlyColorSet } from 'browser/Types';
9
- import { channels, color, css, NULL_COLOR } from 'common/Color';
8
+ import { DEFAULT_ANSI_COLORS, IColorContrastCache, IColorSet, ReadonlyColorSet } from 'browser/Types';
9
+ import { color, css, NULL_COLOR } from 'common/Color';
10
10
  import { EventEmitter } from 'common/EventEmitter';
11
11
  import { Disposable } from 'common/Lifecycle';
12
12
  import { IOptionsService, ITheme } from 'common/services/Services';
@@ -29,54 +29,6 @@ const DEFAULT_SELECTION = {
29
29
  rgba: 0xFFFFFF4D
30
30
  };
31
31
 
32
- // An IIFE to generate DEFAULT_ANSI_COLORS.
33
- export const DEFAULT_ANSI_COLORS = Object.freeze((() => {
34
- const colors = [
35
- // dark:
36
- css.toColor('#2e3436'),
37
- css.toColor('#cc0000'),
38
- css.toColor('#4e9a06'),
39
- css.toColor('#c4a000'),
40
- css.toColor('#3465a4'),
41
- css.toColor('#75507b'),
42
- css.toColor('#06989a'),
43
- css.toColor('#d3d7cf'),
44
- // bright:
45
- css.toColor('#555753'),
46
- css.toColor('#ef2929'),
47
- css.toColor('#8ae234'),
48
- css.toColor('#fce94f'),
49
- css.toColor('#729fcf'),
50
- css.toColor('#ad7fa8'),
51
- css.toColor('#34e2e2'),
52
- css.toColor('#eeeeec')
53
- ];
54
-
55
- // Fill in the remaining 240 ANSI colors.
56
- // Generate colors (16-231)
57
- const v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff];
58
- for (let i = 0; i < 216; i++) {
59
- const r = v[(i / 36) % 6 | 0];
60
- const g = v[(i / 6) % 6 | 0];
61
- const b = v[i % 6];
62
- colors.push({
63
- css: channels.toCss(r, g, b),
64
- rgba: channels.toRgba(r, g, b)
65
- });
66
- }
67
-
68
- // Generate greys (232-255)
69
- for (let i = 0; i < 24; i++) {
70
- const c = 8 + i * 10;
71
- colors.push({
72
- css: channels.toCss(c, c, c),
73
- rgba: channels.toRgba(c, c, c)
74
- });
75
- }
76
-
77
- return colors;
78
- })());
79
-
80
32
  export class ThemeService extends Disposable implements IThemeService {
81
33
  public serviceBrand: undefined;
82
34
 
@@ -20,23 +20,18 @@ export interface IEventEmitter<T, U = void> {
20
20
  }
21
21
 
22
22
  export class EventEmitter<T, U = void> implements IEventEmitter<T, U> {
23
- private _listeners: IListener<T, U>[] = [];
23
+ private _listeners: Set<IListener<T, U>> = new Set();
24
24
  private _event?: IEvent<T, U>;
25
25
  private _disposed: boolean = false;
26
26
 
27
27
  public get event(): IEvent<T, U> {
28
28
  if (!this._event) {
29
29
  this._event = (listener: (arg1: T, arg2: U) => any) => {
30
- this._listeners.push(listener);
30
+ this._listeners.add(listener);
31
31
  const disposable = {
32
32
  dispose: () => {
33
33
  if (!this._disposed) {
34
- for (let i = 0; i < this._listeners.length; i++) {
35
- if (this._listeners[i] === listener) {
36
- this._listeners.splice(i, 1);
37
- return;
38
- }
39
- }
34
+ this._listeners.delete(listener);
40
35
  }
41
36
  }
42
37
  };
@@ -48,8 +43,8 @@ export class EventEmitter<T, U = void> implements IEventEmitter<T, U> {
48
43
 
49
44
  public fire(arg1: T, arg2: U): void {
50
45
  const queue: IListener<T, U>[] = [];
51
- for (let i = 0; i < this._listeners.length; i++) {
52
- queue.push(this._listeners[i]);
46
+ for (const l of this._listeners.values()) {
47
+ queue.push(l);
53
48
  }
54
49
  for (let i = 0; i < queue.length; i++) {
55
50
  queue[i].call(undefined, arg1, arg2);
@@ -63,7 +58,7 @@ export class EventEmitter<T, U = void> implements IEventEmitter<T, U> {
63
58
 
64
59
  public clearListeners(): void {
65
60
  if (this._listeners) {
66
- this._listeners.length = 0;
61
+ this._listeners.clear();
67
62
  }
68
63
  }
69
64
  }