diff --git a/README.md b/README.md index 4ef592c2..0e7df5ac 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**TeleType**](https://github.com/akshaykmr/TeleType): cli tool that allows you to share your terminal online conveniently. Show off mad cli-fu, help a colleague, teach, or troubleshoot. - [**Intervue**](https://www.intervue.io): Pair programming for interviews. Multiple programming languages supported, with results displayed by xterm.js. - [**TRASA**](https://trasa.io): Zero trust access to Web, SSH, RDP and Database services. +- [**Commas**](https://github.com/CyanSalt/commas): Commas is a hackable terminal and command runner. [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. Note: Please add any new contributions to the end of the list only. diff --git a/addons/xterm-addon-ligatures/package.json b/addons/xterm-addon-ligatures/package.json index df85c020..a35cfc5c 100644 --- a/addons/xterm-addon-ligatures/package.json +++ b/addons/xterm-addon-ligatures/package.json @@ -31,7 +31,7 @@ ], "license": "MIT", "dependencies": { - "font-finder": "^1.0.4", + "font-finder": "^1.1.0", "font-ligatures": "^1.3.3" }, "devDependencies": { diff --git a/demo/server.js b/demo/server.js index c04c48ea..c0d5e1f6 100644 --- a/demo/server.js +++ b/demo/server.js @@ -43,15 +43,15 @@ function startServer() { const env = Object.assign({}, process.env); env['COLORTERM'] = 'truecolor'; var cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], { - name: 'xterm-256color', - cols: cols || 80, - rows: rows || 24, - cwd: env.PWD, - env: env, - encoding: USE_BINARY ? null : 'utf8' - }); + rows = parseInt(req.query.rows), + term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], { + name: 'xterm-256color', + cols: cols || 80, + rows: rows || 24, + cwd: process.platform === 'win32' ? undefined : env.PWD, + env: env, + encoding: USE_BINARY ? null : 'utf8' + }); console.log('Created terminal with PID: ' + term.pid); terminals[term.pid] = term; diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index c55aaad9..e5cbb372 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -263,7 +263,7 @@ export class AccessibilityManager extends Disposable { const element = this._rowElements[i]; if (element) { if (lineData.length === 0) { - element.innerHTML = ' '; + element.innerText = '\u00a0'; } else { element.textContent = lineData; } diff --git a/src/browser/ColorManager.ts b/src/browser/ColorManager.ts index 9a0fd7ee..b6950d28 100644 --- a/src/browser/ColorManager.ts +++ b/src/browser/ColorManager.ts @@ -17,9 +17,8 @@ const DEFAULT_SELECTION = { rgba: 0xFFFFFF4D }; -// An IIFE to generate DEFAULT_ANSI_COLORS. Do not mutate DEFAULT_ANSI_COLORS, instead make a copy -// and mutate that. -export const DEFAULT_ANSI_COLORS = (() => { +// An IIFE to generate DEFAULT_ANSI_COLORS. +export const DEFAULT_ANSI_COLORS = Object.freeze((() => { const colors = [ // dark: css.toColor('#2e3436'), @@ -64,7 +63,7 @@ export const DEFAULT_ANSI_COLORS = (() => { } return colors; -})(); +})()); /** * Manages the source of truth for a terminal's colors. diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 64c65fe9..70247f88 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -13,11 +13,13 @@ import * as Strings from '../LocalizableStrings'; import { IEvent, EventEmitter } from 'common/EventEmitter'; import { AddonManager } from './AddonManager'; import { IParams } from 'common/parser/Types'; +import { BufferSet } from 'common/buffer/BufferSet'; export class Terminal implements ITerminalApi { private _core: ITerminal; private _addonManager: AddonManager; private _parser: IParser | undefined; + private _buffer: BufferNamespaceApi | undefined; constructor(options?: ITerminalOptions) { this._core = new TerminalCore(options); @@ -58,7 +60,10 @@ export class Terminal implements ITerminalApi { public get cols(): number { return this._core.cols; } public get buffer(): IBufferNamespaceApi { this._checkProposedApi(); - return new BufferNamespaceApi(this._core.buffers); + if (!this._buffer) { + this._buffer = new BufferNamespaceApi(this._core); + } + return this._buffer; } public get markers(): ReadonlyArray { this._checkProposedApi(); @@ -245,21 +250,21 @@ class BufferNamespaceApi implements IBufferNamespaceApi { private _onBufferChange = new EventEmitter(); public get onBufferChange(): IEvent { return this._onBufferChange.event; } - constructor(private _buffers: IBufferSet) { - this._normal = new BufferApiView(this._buffers.normal, 'normal'); - this._alternate = new BufferApiView(this._buffers.alt, 'alternate'); - this._buffers.onBufferActivate(() => this._onBufferChange.fire(this.active)); + constructor(private _core: ITerminal) { + this._normal = new BufferApiView(this._core.buffers.normal, 'normal'); + this._alternate = new BufferApiView(this._core.buffers.alt, 'alternate'); + this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active)); } public get active(): IBufferApi { - if (this._buffers.active === this._buffers.normal) { return this.normal; } - if (this._buffers.active === this._buffers.alt) { return this.alternate; } + if (this._core.buffers.active === this._core.buffers.normal) { return this.normal; } + if (this._core.buffers.active === this._core.buffers.alt) { return this.alternate; } throw new Error('Active buffer is neither normal nor alternate'); } public get normal(): IBufferApi { - return this._normal.init(this._buffers.normal); + return this._normal.init(this._core.buffers.normal); } public get alternate(): IBufferApi { - return this._alternate.init(this._buffers.alt); + return this._alternate.init(this._core.buffers.alt); } } diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index ed2c340b..f0a92259 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -138,7 +138,7 @@ export class DomRenderer extends Disposable implements IRenderer { ` width: ${this.dimensions.actualCellWidth}px` + `}`; - this._dimensionsStyleElement.innerHTML = styles; + this._dimensionsStyleElement.textContent = styles; this._selectionContainer.style.height = this._viewportElement.style.height; this._screenElement.style.width = `${this.dimensions.canvasWidth}px`; @@ -237,7 +237,7 @@ export class DomRenderer extends Disposable implements IRenderer { `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(this._colors.background).css}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`; - this._themeStyleElement.innerHTML = styles; + this._themeStyleElement.textContent = styles; } public onDevicePixelRatioChange(): void { @@ -348,7 +348,7 @@ export class DomRenderer extends Disposable implements IRenderer { public clear(): void { for (const e of this._rowElements) { - e.innerHTML = ''; + e.innerText = ''; } } @@ -359,7 +359,7 @@ export class DomRenderer extends Disposable implements IRenderer { for (let y = start; y <= end; y++) { const rowElement = this._rowElements[y]; - rowElement.innerHTML = ''; + rowElement.innerText = ''; const row = y + this._bufferService.buffer.ydisp; const lineData = this._bufferService.buffer.lines.get(row); diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts index b9dc7995..b74c4eac 100644 --- a/src/common/buffer/BufferSet.ts +++ b/src/common/buffer/BufferSet.ts @@ -15,10 +15,9 @@ import { Disposable } from 'common/Lifecycle'; * provides also utilities for working with them. */ export class BufferSet extends Disposable implements IBufferSet { - private _normal: Buffer; - private _alt: Buffer; - private _activeBuffer: Buffer; - + private _normal!: Buffer; + private _alt!: Buffer; + private _activeBuffer!: Buffer; private _onBufferActivate = this.register(new EventEmitter<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}>()); public get onBufferActivate(): IEvent<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}> { return this._onBufferActivate.event; } @@ -28,17 +27,20 @@ export class BufferSet extends Disposable implements IBufferSet { * @param _terminal - The terminal the BufferSet will belong to */ constructor( - optionsService: IOptionsService, - bufferService: IBufferService + private readonly _optionsService: IOptionsService, + private readonly _bufferService: IBufferService ) { super(); + this.reset(); + } - this._normal = new Buffer(true, optionsService, bufferService); + public reset(): void { + this._normal = new Buffer(true, this._optionsService, this._bufferService); this._normal.fillViewportRows(); // The alt buffer should never have scrollback. // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer - this._alt = new Buffer(false, optionsService, bufferService); + this._alt = new Buffer(false, this._optionsService, this._bufferService); this._activeBuffer = this._normal; this.setupTabStops(); diff --git a/src/common/buffer/Types.d.ts b/src/common/buffer/Types.d.ts index 752b1a26..cbf40a03 100644 --- a/src/common/buffer/Types.d.ts +++ b/src/common/buffer/Types.d.ts @@ -56,6 +56,7 @@ export interface IBufferSet extends IDisposable { activateNormalBuffer(): void; activateAltBuffer(fillAttr?: IAttributeData): void; + reset(): void; resize(newCols: number, newRows: number): void; setupTabStops(i?: number): void; } diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 301146e1..47e54729 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -49,8 +49,7 @@ export class BufferService extends Disposable implements IBufferService { } public reset(): void { - this.buffers.dispose(); - this.buffers = new BufferSet(this._optionsService, this); + this.buffers.reset(); this.isUserScrolling = false; } }