Merge branch 'master' into fix/memory-leak
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"name": "xterm.js",
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node:0-18-buster",
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node:18-bookworm",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {} // yarn
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": 18
|
||||
} // yarn
|
||||
},
|
||||
"forwardPorts": [
|
||||
3000
|
||||
],
|
||||
"postCreateCommand": "yarn install",
|
||||
"postCreateCommand": "yarn install && yarn setup",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
|
||||
Vendored
+1
-1
@@ -61,7 +61,7 @@
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": ["start"],
|
||||
"stopOnEntry": true,
|
||||
"runtimeVersion": "16",
|
||||
"runtimeVersion": "18",
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "App listening to (http://.*?:[0-9]+)"
|
||||
|
||||
@@ -21,7 +21,7 @@ Xterm.js is a front-end component written in TypeScript that lets applications b
|
||||
First, you need to install the module, we ship exclusively through [npm](https://www.npmjs.com/), so you need that installed and then add xterm.js as a dependency by running:
|
||||
|
||||
```bash
|
||||
npm install xterm
|
||||
npm install @xterm/xterm
|
||||
```
|
||||
|
||||
To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to the head of your HTML page. Then create a `<div id="terminal"></div>` onto which xterm can attach itself. Finally, instantiate the `Terminal` object and then call the `open` function with the DOM object of the `div`.
|
||||
@@ -30,8 +30,8 @@ To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to t
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="node_modules/xterm/css/xterm.css" />
|
||||
<script src="node_modules/xterm/lib/xterm.js"></script>
|
||||
<link rel="stylesheet" href="node_modules/@xterm/xterm/css/xterm.css" />
|
||||
<script src="node_modules/@xterm/xterm/lib/xterm.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="terminal"></div>
|
||||
@@ -113,7 +113,7 @@ All current and past releases are available on this repo's [Releases page](https
|
||||
Our CI releases beta builds to npm for every change that goes into master. Install the latest beta build with:
|
||||
|
||||
```bash
|
||||
npm install -S xterm@beta
|
||||
npm install -S @xterm/xterm@beta
|
||||
```
|
||||
|
||||
These should generally be stable, but some bugs may slip in. We recommend using the beta build primarily to test out new features and to verify bug fixes.
|
||||
@@ -222,6 +222,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
||||
- [**Cloudtutor.io**](https://cloudtutor.io): innovative online learning platform that offers users access to an interactive lab.
|
||||
- [**Helix Editor Playground**](https://github.com/tomgroenwoldt/helix-editor-playground): Online playground for the terminal based helix editor.
|
||||
- [**Coder**](https://github.com/coder/coder): Self-Hosted Remote Development Environments
|
||||
- [**Wave Terminal**](https://waveterm.dev): An open-source, ai-native, terminal built for seamless workflows.
|
||||
- [And much more...](https://github.com/xtermjs/xterm.js/network/dependents?package_id=UGFja2FnZS0xNjYzMjc4OQ%3D%3D)
|
||||
|
||||
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 on our list. Note: Please add any new contributions to the end of the list only.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-attach",
|
||||
"version": "0.9.0",
|
||||
"version": "0.11.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -25,7 +25,9 @@ module.exports = {
|
||||
filename: mainFile,
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-canvas",
|
||||
"version": "0.5.0",
|
||||
"version": "0.7.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -8,7 +8,7 @@ import { CellColorResolver } from 'browser/renderer/shared/CellColorResolver';
|
||||
import { acquireTextureAtlas } from 'browser/renderer/shared/CharAtlasCache';
|
||||
import { TEXT_BASELINE } from 'browser/renderer/shared/Constants';
|
||||
import { tryDrawCustomChar } from 'browser/renderer/shared/CustomGlyphs';
|
||||
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
||||
import { allowRescaling, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
||||
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
|
||||
import { IRasterizedGlyph, IRenderDimensions, ISelectionRenderModel, ITextureAtlas } from 'browser/renderer/shared/Types';
|
||||
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
||||
@@ -365,6 +365,8 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
|
||||
*/
|
||||
protected _drawChars(cell: ICellData, x: number, y: number): void {
|
||||
const chars = cell.getChars();
|
||||
const code = cell.getCode();
|
||||
const width = cell.getWidth();
|
||||
this._cellColorResolver.resolve(cell, x, this._bufferService.buffer.ydisp + y, this._deviceCellWidth);
|
||||
|
||||
if (!this._charAtlas) {
|
||||
@@ -400,6 +402,16 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
|
||||
this._bitmapGenerator[glyph.texturePage]!.refresh();
|
||||
this._bitmapGenerator[glyph.texturePage]!.version = this._charAtlas.pages[glyph.texturePage].version;
|
||||
}
|
||||
|
||||
// Reduce scale horizontally for wide glyphs printed in cells that would overlap with the
|
||||
// following cell (ie. the width is not 2).
|
||||
let renderWidth = glyph.size.x;
|
||||
if (this._optionsService.rawOptions.rescaleOverlappingGlyphs) {
|
||||
if (allowRescaling(code, width, glyph.size.x, this._deviceCellWidth)) {
|
||||
renderWidth = this._deviceCellWidth - 1; // - 1 to improve readability
|
||||
}
|
||||
}
|
||||
|
||||
this._ctx.drawImage(
|
||||
this._bitmapGenerator[glyph.texturePage]?.bitmap || this._charAtlas!.pages[glyph.texturePage].canvas,
|
||||
glyph.texturePosition.x,
|
||||
@@ -408,7 +420,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
|
||||
glyph.size.y,
|
||||
x * this._deviceCellWidth + this._deviceCharLeft - glyph.offset.x,
|
||||
y * this._deviceCellHeight + this._deviceCharTop - glyph.offset.y,
|
||||
glyph.size.x,
|
||||
renderWidth,
|
||||
glyph.size.y
|
||||
);
|
||||
this._ctx.restore();
|
||||
|
||||
@@ -33,7 +33,9 @@ module.exports = {
|
||||
filename: mainFile,
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-fit",
|
||||
"version": "0.8.0",
|
||||
"version": "0.10.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -25,7 +25,9 @@ module.exports = {
|
||||
filename: mainFile,
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-image",
|
||||
"version": "0.6.0",
|
||||
"version": "0.8.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -33,7 +33,9 @@ const addon = {
|
||||
filename: mainFile,
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-ligatures",
|
||||
"version": "0.7.0",
|
||||
"version": "0.9.0",
|
||||
"description": "Add support for programming ligatures to xterm.js",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
|
||||
@@ -25,7 +25,9 @@ module.exports = {
|
||||
filename: mainFile,
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production',
|
||||
externals: {
|
||||
|
||||
@@ -86,9 +86,9 @@ fd-slicer@~1.1.0:
|
||||
pend "~1.2.0"
|
||||
|
||||
follow-redirects@^1.15.0:
|
||||
version "1.15.3"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
|
||||
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
|
||||
version "1.15.6"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
|
||||
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
|
||||
|
||||
font-finder@^1.0.3:
|
||||
version "1.0.4"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-search",
|
||||
"version": "0.13.0",
|
||||
"version": "0.15.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import type { Terminal, IDisposable, ITerminalAddon, IDecoration } from '@xterm/xterm';
|
||||
import type { SearchAddon as ISearchApi } from '@xterm/addon-search';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { Disposable, toDisposable, disposeArray, MutableDisposable } from 'common/Lifecycle';
|
||||
import { Disposable, toDisposable, disposeArray, MutableDisposable, getDisposeArrayDisposable } from 'common/Lifecycle';
|
||||
|
||||
export interface ISearchOptions {
|
||||
regex?: boolean;
|
||||
@@ -78,8 +78,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
|
||||
*/
|
||||
private _linesCache: LineCacheEntry[] | undefined;
|
||||
private _linesCacheTimeoutId = 0;
|
||||
private _cursorMoveListener: IDisposable | undefined;
|
||||
private _resizeListener: IDisposable | undefined;
|
||||
private _linesCacheDisposables = new MutableDisposable();
|
||||
|
||||
private readonly _onDidChangeResults = this.register(new EventEmitter<{ resultIndex: number, resultCount: number }>());
|
||||
public readonly onDidChangeResults = this._onDidChangeResults.event;
|
||||
@@ -427,8 +426,11 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
|
||||
const terminal = this._terminal!;
|
||||
if (!this._linesCache) {
|
||||
this._linesCache = new Array(terminal.buffer.active.length);
|
||||
this._cursorMoveListener = terminal.onCursorMove(() => this._destroyLinesCache());
|
||||
this._resizeListener = terminal.onResize(() => this._destroyLinesCache());
|
||||
this._linesCacheDisposables.value = getDisposeArrayDisposable([
|
||||
terminal.onLineFeed(() => this._destroyLinesCache()),
|
||||
terminal.onCursorMove(() => this._destroyLinesCache()),
|
||||
terminal.onResize(() => this._destroyLinesCache())
|
||||
]);
|
||||
}
|
||||
|
||||
window.clearTimeout(this._linesCacheTimeoutId);
|
||||
@@ -437,14 +439,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA
|
||||
|
||||
private _destroyLinesCache(): void {
|
||||
this._linesCache = undefined;
|
||||
if (this._cursorMoveListener) {
|
||||
this._cursorMoveListener.dispose();
|
||||
this._cursorMoveListener = undefined;
|
||||
}
|
||||
if (this._resizeListener) {
|
||||
this._resizeListener.dispose();
|
||||
this._resizeListener = undefined;
|
||||
}
|
||||
this._linesCacheDisposables.clear();
|
||||
if (this._linesCacheTimeoutId) {
|
||||
window.clearTimeout(this._linesCacheTimeoutId);
|
||||
this._linesCacheTimeoutId = 0;
|
||||
|
||||
@@ -32,7 +32,9 @@ module.exports = {
|
||||
filename: mainFile,
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-serialize",
|
||||
"version": "0.11.0",
|
||||
"version": "0.13.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -34,7 +34,8 @@ module.exports = {
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd',
|
||||
globalObject: 'this'
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-unicode-graphemes",
|
||||
"version": "0.1.0",
|
||||
"version": "0.3.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -32,7 +32,9 @@ module.exports = {
|
||||
filename: mainFile,
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-unicode11",
|
||||
"version": "0.6.0",
|
||||
"version": "0.8.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -33,7 +33,8 @@ module.exports = {
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd',
|
||||
globalObject: 'this'
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-web-links",
|
||||
"version": "0.9.0",
|
||||
"version": "0.11.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -41,6 +41,20 @@ export class WebLinkProvider implements ILinkProvider {
|
||||
}
|
||||
}
|
||||
|
||||
function isUrl(urlString: string): boolean {
|
||||
try {
|
||||
const url = new URL(urlString);
|
||||
const parsedBase = url.password && url.username
|
||||
? `${url.protocol}//${url.username}:${url.password}@${url.host}`
|
||||
: url.username
|
||||
? `${url.protocol}//${url.username}@${url.host}`
|
||||
: `${url.protocol}//${url.host}`;
|
||||
return urlString.toLocaleLowerCase().startsWith(parsedBase.toLocaleLowerCase());
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class LinkComputer {
|
||||
public static computeLink(y: number, regex: RegExp, terminal: Terminal, activate: (event: MouseEvent, uri: string) => void): ILink[] {
|
||||
const rex = new RegExp(regex.source, (regex.flags || '') + 'g');
|
||||
@@ -55,20 +69,7 @@ export class LinkComputer {
|
||||
const text = match[0];
|
||||
|
||||
// check via URL if the matched text would form a proper url
|
||||
// NOTE: This outsources the ugly url parsing to the browser.
|
||||
// To avoid surprising auto expansion from URL we additionally
|
||||
// check afterwards if the provided string resembles the parsed
|
||||
// one close enough:
|
||||
// - decodeURI decode path segement back to byte repr
|
||||
// to detect unicode auto conversion correctly
|
||||
// - append / also match domain urls w'o any path notion
|
||||
try {
|
||||
const url = new URL(text);
|
||||
const urlText = decodeURI(url.toString());
|
||||
if (text !== urlText && text + '/' !== urlText) {
|
||||
continue;
|
||||
}
|
||||
} catch (e) {
|
||||
if (!isUrl(text)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider';
|
||||
// - final interpunction like ,.!?
|
||||
// - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls)
|
||||
// - unsafe chars from rfc1738: {}|\^~[]`
|
||||
const strictUrlRegex = /https?:[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/;
|
||||
const strictUrlRegex = /(https?|HTTPS?):[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/;
|
||||
|
||||
|
||||
function handleLink(event: MouseEvent, uri: string): void {
|
||||
|
||||
@@ -115,6 +115,28 @@ describe('WebLinksAddon', () => {
|
||||
await resetAndHover(5, 1);
|
||||
await evalLinkStateData('http://test:password@example.com/some_path', { start: { x: 12, y: 1 }, end: { x: 13, y: 2 } });
|
||||
});
|
||||
it('url encoded params work properly', async () => {
|
||||
await writeSync(page, '¥¥¥cafe\u0301 http://test:password@example.com/some_path?param=1%202%3');
|
||||
await resetAndHover(12, 0);
|
||||
await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } });
|
||||
await resetAndHover(5, 1);
|
||||
await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } });
|
||||
});
|
||||
});
|
||||
|
||||
// issue #4964
|
||||
it('uppercase in protocol and host, default ports', async () => {
|
||||
const data = ` HTTP://EXAMPLE.COM \\r\\n` +
|
||||
` HTTPS://Example.com \\r\\n` +
|
||||
` HTTP://Example.com:80 \\r\\n` +
|
||||
` HTTP://Example.com:80/staysUpper \\r\\n` +
|
||||
` HTTP://Ab:xY@abc.com:80/staysUpper \\r\\n`;
|
||||
await writeSync(page, data);
|
||||
await pollForLinkAtCell(3, 0, `HTTP://EXAMPLE.COM`);
|
||||
await pollForLinkAtCell(3, 1, `HTTPS://Example.com`);
|
||||
await pollForLinkAtCell(3, 2, `HTTP://Example.com:80`);
|
||||
await pollForLinkAtCell(3, 3, `HTTP://Example.com:80/staysUpper`);
|
||||
await pollForLinkAtCell(3, 4, `HTTP://Ab:xY@abc.com:80/staysUpper`);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@ module.exports = {
|
||||
filename: mainFile,
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xterm/addon-webgl",
|
||||
"version": "0.16.0",
|
||||
"version": "0.18.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
||||
import { allowRescaling, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
||||
import { TextureAtlas } from 'browser/renderer/shared/TextureAtlas';
|
||||
import { IRasterizedGlyph, IRenderDimensions, ITextureAtlas } from 'browser/renderer/shared/Types';
|
||||
import { NULL_CELL_CODE } from 'common/buffer/Constants';
|
||||
@@ -11,6 +11,7 @@ import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types';
|
||||
import { createProgram, GLTexture, PROJECTION_MATRIX } from './WebglUtils';
|
||||
import type { IOptionsService } from 'common/services/Services';
|
||||
|
||||
interface IVertices {
|
||||
attributes: Float32Array;
|
||||
@@ -111,7 +112,8 @@ export class GlyphRenderer extends Disposable {
|
||||
constructor(
|
||||
private readonly _terminal: Terminal,
|
||||
private readonly _gl: IWebGL2RenderingContext,
|
||||
private _dimensions: IRenderDimensions
|
||||
private _dimensions: IRenderDimensions,
|
||||
private readonly _optionsService: IOptionsService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -212,15 +214,15 @@ export class GlyphRenderer extends Disposable {
|
||||
return this._atlas ? this._atlas.beginFrame() : true;
|
||||
}
|
||||
|
||||
public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, lastBg: number): void {
|
||||
public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void {
|
||||
// Since this function is called for every cell (`rows*cols`), it must be very optimized. It
|
||||
// should not instantiate any variables unless a new glyph is drawn to the cache where the
|
||||
// slight slowdown is acceptable for the developer ergonomics provided as it's a once of for
|
||||
// each glyph.
|
||||
this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, lastBg);
|
||||
this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, width, lastBg);
|
||||
}
|
||||
|
||||
private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, ext: number, chars: string, lastBg: number): void {
|
||||
private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void {
|
||||
$i = (y * this._terminal.cols + x) * INDICES_PER_CELL;
|
||||
|
||||
// Exit early if this is a null character, allow space character to continue as it may have
|
||||
@@ -275,6 +277,14 @@ export class GlyphRenderer extends Disposable {
|
||||
array[$i + 8] = $glyph.sizeClipSpace.y;
|
||||
}
|
||||
// a_cellpos only changes on resize
|
||||
|
||||
// Reduce scale horizontally for wide glyphs printed in cells that would overlap with the
|
||||
// following cell (ie. the width is not 2).
|
||||
if (this._optionsService.rawOptions.rescaleOverlappingGlyphs) {
|
||||
if (allowRescaling(code, width, $glyph.size.x, this._dimensions.device.cell.width)) {
|
||||
array[$i + 2] = (this._dimensions.device.cell.width - 1) / this._dimensions.device.canvas.width; // - 1 to improve readability
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
|
||||
@@ -36,7 +36,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
private _observerDisposable = this.register(new MutableDisposable());
|
||||
|
||||
private _model: RenderModel = new RenderModel();
|
||||
private _workCell: CellData = new CellData();
|
||||
private _workCell: ICellData = new CellData();
|
||||
private _workCell2: ICellData = new CellData();
|
||||
private _cellColorResolver: CellColorResolver;
|
||||
|
||||
private _canvas: HTMLCanvasElement;
|
||||
@@ -245,7 +246,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
*/
|
||||
private _initializeWebGLState(): [RectangleRenderer, GlyphRenderer] {
|
||||
this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService);
|
||||
this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions);
|
||||
this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions, this._optionsService);
|
||||
|
||||
// Update dimensions and acquire char atlas
|
||||
this.handleCharSizeChanged();
|
||||
@@ -388,6 +389,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
let range: [number, number];
|
||||
let chars: string;
|
||||
let code: number;
|
||||
let width: number;
|
||||
let i: number;
|
||||
let x: number;
|
||||
let j: number;
|
||||
@@ -500,7 +502,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
|
||||
this._model.cells[i + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
|
||||
|
||||
this._glyphRenderer.value!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, lastBg);
|
||||
width = cell.getWidth();
|
||||
this._glyphRenderer.value!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, width, lastBg);
|
||||
|
||||
if (isJoined) {
|
||||
// Restore work cell
|
||||
@@ -509,7 +512,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
// Null out non-first cells
|
||||
for (x++; x < lastCharX; x++) {
|
||||
j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
||||
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0);
|
||||
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0);
|
||||
this._model.cells[j] = NULL_CELL_CODE;
|
||||
this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg;
|
||||
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
|
||||
|
||||
@@ -33,7 +33,9 @@ module.exports = {
|
||||
filename: mainFile,
|
||||
path: path.resolve('./lib'),
|
||||
library: addonName,
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
};
|
||||
|
||||
+2
-3
@@ -57,9 +57,8 @@ function checkAndPublishPackage(packageDir) {
|
||||
const packageJson = require(path.join(packageDir, 'package.json'));
|
||||
|
||||
// Determine if this is a stable or beta release
|
||||
// TODO: Uncomment when publishing 5.4
|
||||
// const publishedVersions = getPublishedVersions(packageJson);
|
||||
const isStableRelease = false; //!publishedVersions.includes(packageJson.version);
|
||||
const publishedVersions = getPublishedVersions(packageJson);
|
||||
const isStableRelease = !publishedVersions.includes(packageJson.version);
|
||||
|
||||
// Get the next version
|
||||
let nextVersion = isStableRelease ? packageJson.version : getNextBetaVersion(packageJson);
|
||||
|
||||
+29
-1
@@ -44,7 +44,7 @@ if ('WebAssembly' in window) {
|
||||
|
||||
// Pulling in the module's types relies on the <reference> above, it's looks a
|
||||
// little weird here as we're importing "this" module
|
||||
import { Terminal as TerminalType, ITerminalOptions } from '@xterm/xterm';
|
||||
import { Terminal as TerminalType, ITerminalOptions, type IDisposable } from '@xterm/xterm';
|
||||
|
||||
export interface IWindowWithTerminal extends Window {
|
||||
term: TerminalType;
|
||||
@@ -255,6 +255,7 @@ if (document.location.pathname === '/test') {
|
||||
document.getElementById('add-grapheme-clusters').addEventListener('click', addGraphemeClusters);
|
||||
document.getElementById('add-decoration').addEventListener('click', addDecoration);
|
||||
document.getElementById('add-overview-ruler').addEventListener('click', addOverviewRuler);
|
||||
document.getElementById('decoration-stress-test').addEventListener('click', decorationStressTest);
|
||||
document.getElementById('weblinks-test').addEventListener('click', testWeblinks);
|
||||
document.getElementById('bce').addEventListener('click', coloredErase);
|
||||
addVtButtons();
|
||||
@@ -1170,6 +1171,33 @@ function addOverviewRuler(): void {
|
||||
term.registerDecoration({ marker: term.registerMarker(10), overviewRulerOptions: { color: '#ffffff80', position: 'full' } });
|
||||
}
|
||||
|
||||
let decorationStressTestDecorations: IDisposable[] | undefined;
|
||||
function decorationStressTest(): void {
|
||||
if (decorationStressTestDecorations) {
|
||||
for (const d of decorationStressTestDecorations) {
|
||||
d.dispose();
|
||||
}
|
||||
decorationStressTestDecorations = undefined;
|
||||
} else {
|
||||
const t = term as Terminal;
|
||||
const buffer = t.buffer.active;
|
||||
const cursorY = buffer.baseY + buffer.cursorY;
|
||||
decorationStressTestDecorations = [];
|
||||
for (const x of [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]) {
|
||||
for (let y = 0; y < t.buffer.active.length; y++) {
|
||||
const cursorOffsetY = y - cursorY;
|
||||
decorationStressTestDecorations.push(t.registerDecoration({
|
||||
marker: t.registerMarker(cursorOffsetY),
|
||||
x,
|
||||
width: 4,
|
||||
backgroundColor: '#FF0000',
|
||||
overviewRulerOptions: { color: '#FF0000' }
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(console as any).image = (source: ImageData | HTMLCanvasElement, scale: number = 1) => {
|
||||
function getBox(width: number, height: number): any {
|
||||
return {
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
<dt>Decorations</dt>
|
||||
<dd><button id="add-decoration" title="Add a decoration to the terminal">Decoration</button></dd>
|
||||
<dd><button id="add-overview-ruler" title="Add an overview ruler to the terminal">Add Overview Ruler</button></dd>
|
||||
<dd><button id="decoration-stress-test" title="Toggle between adding and removing a decoration to each line">Stress Test</button></dd>
|
||||
|
||||
<dt>Weblinks Addon</dt>
|
||||
<dd><button id="weblinks-test" title="Various url conditions from demo data, hover&click to test">Test URLs</button></dd>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@xterm/xterm",
|
||||
"description": "Full xterm terminal, in your browser",
|
||||
"version": "5.3.0",
|
||||
"version": "5.5.0",
|
||||
"main": "lib/xterm.js",
|
||||
"style": "css/xterm.css",
|
||||
"types": "typings/xterm.d.ts",
|
||||
|
||||
@@ -72,6 +72,9 @@ export class MockTerminal implements ITerminal {
|
||||
public focus(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public input(data: string, wasUserInput: boolean = true): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public resize(columns: number, rows: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
+15
-2
@@ -36,6 +36,8 @@ export class Viewport extends Disposable implements IViewport {
|
||||
private _activeBuffer: IBuffer;
|
||||
private _renderDimensions: IRenderDimensions;
|
||||
|
||||
private _smoothScrollAnimationFrame: number = 0;
|
||||
|
||||
// Stores a partial line amount when scrolling, this is used to keep track of how much of a line
|
||||
// is scrolled so we can "scroll" over partial lines and feel natural on touchpads. This is a
|
||||
// quick fix and could have a more robust solution in place that reset the value when needed.
|
||||
@@ -49,6 +51,8 @@ export class Viewport extends Disposable implements IViewport {
|
||||
target: -1
|
||||
};
|
||||
|
||||
private _ensureTimeout: number;
|
||||
|
||||
private readonly _onRequestScrollLines = this.register(new EventEmitter<{ amount: number, suppressScrollEvent: boolean }>());
|
||||
public readonly onRequestScrollLines = this._onRequestScrollLines.event;
|
||||
|
||||
@@ -81,7 +85,7 @@ export class Viewport extends Disposable implements IViewport {
|
||||
this.register(this._optionsService.onSpecificOptionChange('scrollback', () => this.syncScrollArea()));
|
||||
|
||||
// Perform this async to ensure the ICharSizeService is ready.
|
||||
setTimeout(() => this.syncScrollArea());
|
||||
this._ensureTimeout = window.setTimeout(() => this.syncScrollArea());
|
||||
}
|
||||
|
||||
private _handleThemeChange(colors: ReadonlyColorSet): void {
|
||||
@@ -211,7 +215,12 @@ export class Viewport extends Disposable implements IViewport {
|
||||
|
||||
// Continue or finish smooth scroll
|
||||
if (percent < 1) {
|
||||
this._coreBrowserService.window.requestAnimationFrame(() => this._smoothScroll());
|
||||
if (!this._smoothScrollAnimationFrame) {
|
||||
this._smoothScrollAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
||||
this._smoothScrollAnimationFrame = 0;
|
||||
this._smoothScroll();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this._clearSmoothScrollState();
|
||||
}
|
||||
@@ -398,4 +407,8 @@ export class Viewport extends Disposable implements IViewport {
|
||||
this._viewportElement.scrollTop += deltaY;
|
||||
return this._bubbleScroll(ev, deltaY);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
clearTimeout(this._ensureTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +159,9 @@ export class CompositionHelper {
|
||||
// otherwise input characters can be duplicated. (Issue #3191)
|
||||
currentCompositionPosition.start += this._dataAlreadySent.length;
|
||||
if (this._isComposing) {
|
||||
// Use the end position to get the string if a new composition has started.
|
||||
input = this._textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);
|
||||
// Use the start position of the new composition to get the string
|
||||
// if a new composition has started.
|
||||
input = this._textarea.value.substring(currentCompositionPosition.start, this._compositionPosition.start);
|
||||
} else {
|
||||
// Don't use the end position here in order to pick up any characters after the
|
||||
// composition has finished, for example when typing a non-composition character
|
||||
|
||||
@@ -20,6 +20,8 @@ import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOption
|
||||
*/
|
||||
const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows'];
|
||||
|
||||
let $value = 0;
|
||||
|
||||
export class Terminal extends Disposable implements ITerminalApi {
|
||||
private _core: ITerminal;
|
||||
private _addonManager: AddonManager;
|
||||
@@ -138,6 +140,9 @@ export class Terminal extends Disposable implements ITerminalApi {
|
||||
public focus(): void {
|
||||
this._core.focus();
|
||||
}
|
||||
public input(data: string, wasUserInput: boolean = true): void {
|
||||
this._core.input(data, wasUserInput);
|
||||
}
|
||||
public resize(columns: number, rows: number): void {
|
||||
this._verifyIntegers(columns, rows);
|
||||
this._core.resize(columns, rows);
|
||||
@@ -246,16 +251,16 @@ export class Terminal extends Disposable implements ITerminalApi {
|
||||
}
|
||||
|
||||
private _verifyIntegers(...values: number[]): void {
|
||||
for (const value of values) {
|
||||
if (value === Infinity || isNaN(value) || value % 1 !== 0) {
|
||||
for ($value of values) {
|
||||
if ($value === Infinity || isNaN($value) || $value % 1 !== 0) {
|
||||
throw new Error('This API only accepts integers');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _verifyPositiveIntegers(...values: number[]): void {
|
||||
for (const value of values) {
|
||||
if (value && (value === Infinity || isNaN(value) || value % 1 !== 0 || value < 0)) {
|
||||
for ($value of values) {
|
||||
if ($value && ($value === Infinity || isNaN($value) || $value % 1 !== 0 || $value < 0)) {
|
||||
throw new Error('This API only accepts positive integers');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,14 +183,23 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
` font-style: italic;` +
|
||||
`}`;
|
||||
// Blink animation
|
||||
const blinkAnimationUnderlineId = `blink_underline_${this._terminalClass}`;
|
||||
const blinkAnimationBarId = `blink_bar_${this._terminalClass}`;
|
||||
const blinkAnimationBlockId = `blink_block_${this._terminalClass}`;
|
||||
styles +=
|
||||
`@keyframes blink_box_shadow` + `_` + this._terminalClass + ` {` +
|
||||
`@keyframes ${blinkAnimationUnderlineId} {` +
|
||||
` 50% {` +
|
||||
` border-bottom-style: hidden;` +
|
||||
` }` +
|
||||
`}`;
|
||||
styles +=
|
||||
`@keyframes blink_block` + `_` + this._terminalClass + ` {` +
|
||||
`@keyframes ${blinkAnimationBarId} {` +
|
||||
` 50% {` +
|
||||
` box-shadow: none;` +
|
||||
` }` +
|
||||
`}`;
|
||||
styles +=
|
||||
`@keyframes ${blinkAnimationBlockId} {` +
|
||||
` 0% {` +
|
||||
` background-color: ${colors.cursor.css};` +
|
||||
` color: ${colors.cursorAccent.css};` +
|
||||
@@ -202,13 +211,23 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
`}`;
|
||||
// Cursor
|
||||
styles +=
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}:not(.${RowCss.CURSOR_STYLE_BLOCK_CLASS}) {` +
|
||||
` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
||||
` animation: ${blinkAnimationUnderlineId} 1s step-end infinite;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
|
||||
` animation: ${blinkAnimationBarId} 1s step-end infinite;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` +
|
||||
` animation: ${blinkAnimationBlockId} 1s step-end infinite;` +
|
||||
`}` +
|
||||
// !important helps fix an issue where the cursor will not render on top of the selection,
|
||||
// however it's very hard to fix this issue and retain the blink animation without the use of
|
||||
// !important. So this edge case fails when cursor blink is on.
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
` background-color: ${colors.cursor.css};` +
|
||||
` color: ${colors.cursorAccent.css};` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS}:not(.${RowCss.CURSOR_BLINK_CLASS}) {` +
|
||||
` background-color: ${colors.cursor.css} !important;` +
|
||||
` color: ${colors.cursorAccent.css} !important;` +
|
||||
`}` +
|
||||
@@ -324,6 +343,9 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
this._selectionRenderModel.update(this._terminal, start, end, columnSelectMode);
|
||||
if (!this._selectionRenderModel.hasSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Translate from buffer position to viewport position
|
||||
const viewportStartRow = this._selectionRenderModel.viewportStartRow;
|
||||
@@ -331,11 +353,6 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
const viewportCappedStartRow = this._selectionRenderModel.viewportCappedStartRow;
|
||||
const viewportCappedEndRow = this._selectionRenderModel.viewportCappedEndRow;
|
||||
|
||||
// No need to draw the selection
|
||||
if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the selections
|
||||
const documentFragment = this._document.createDocumentFragment();
|
||||
|
||||
|
||||
@@ -134,9 +134,14 @@ export class WidthCache implements IDisposable {
|
||||
public get(c: string, bold: boolean | number, italic: boolean | number): number {
|
||||
let cp = 0;
|
||||
if (!bold && !italic && c.length === 1 && (cp = c.charCodeAt(0)) < WidthCacheSettings.FLAT_SIZE) {
|
||||
return this._flat[cp] !== WidthCacheSettings.FLAT_UNSET
|
||||
? this._flat[cp]
|
||||
: (this._flat[cp] = this._measure(c, 0));
|
||||
if (this._flat[cp] !== WidthCacheSettings.FLAT_UNSET) {
|
||||
return this._flat[cp];
|
||||
}
|
||||
const width = this._measure(c, 0);
|
||||
if (width > 0) {
|
||||
this._flat[cp] = width;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
let key = c;
|
||||
if (bold) key += 'B';
|
||||
@@ -147,7 +152,9 @@ export class WidthCache implements IDisposable {
|
||||
if (bold) variant |= FontVariant.BOLD;
|
||||
if (italic) variant |= FontVariant.ITALIC;
|
||||
width = this._measure(c, variant);
|
||||
this._holey!.set(key, width);
|
||||
if (width > 0) {
|
||||
this._holey!.set(key, width);
|
||||
}
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ export class CellColorResolver {
|
||||
$bg = this._themeService.colors.ansi[this.result.fg & Attributes.PCOLOR_MASK].rgba;
|
||||
break;
|
||||
case Attributes.CM_RGB:
|
||||
$bg = (this.result.fg & Attributes.RGB_MASK) << 8 | 0xFF;
|
||||
$bg = ((this.result.fg & Attributes.RGB_MASK) << 8) | 0xFF;
|
||||
break;
|
||||
case Attributes.CM_DEFAULT:
|
||||
default:
|
||||
@@ -105,7 +105,7 @@ export class CellColorResolver {
|
||||
$bg = this._themeService.colors.ansi[this.result.bg & Attributes.PCOLOR_MASK].rgba;
|
||||
break;
|
||||
case Attributes.CM_RGB:
|
||||
$bg = this.result.bg & Attributes.RGB_MASK << 8 | 0xFF;
|
||||
$bg = ((this.result.bg & Attributes.RGB_MASK) << 8) | 0xFF;
|
||||
break;
|
||||
// No need to consider default bg color here as it's not possible
|
||||
}
|
||||
@@ -143,7 +143,7 @@ export class CellColorResolver {
|
||||
$fg = this._themeService.colors.ansi[this.result.bg & Attributes.PCOLOR_MASK].rgba;
|
||||
break;
|
||||
case Attributes.CM_RGB:
|
||||
$fg = this.result.bg & Attributes.RGB_MASK << 8 | 0xFF;
|
||||
$fg = ((this.result.bg & Attributes.RGB_MASK) << 8) | 0xFF;
|
||||
break;
|
||||
// No need to consider default bg color here as it's not possible
|
||||
}
|
||||
@@ -154,7 +154,7 @@ export class CellColorResolver {
|
||||
$fg = this._themeService.colors.ansi[this.result.fg & Attributes.PCOLOR_MASK].rgba;
|
||||
break;
|
||||
case Attributes.CM_RGB:
|
||||
$fg = (this.result.fg & Attributes.RGB_MASK) << 8 | 0xFF;
|
||||
$fg = ((this.result.fg & Attributes.RGB_MASK) << 8) | 0xFF;
|
||||
break;
|
||||
case Attributes.CM_DEFAULT:
|
||||
default:
|
||||
|
||||
@@ -23,10 +23,43 @@ export function isRestrictedPowerlineGlyph(codepoint: number): boolean {
|
||||
return 0xE0B0 <= codepoint && codepoint <= 0xE0B7;
|
||||
}
|
||||
|
||||
function isNerdFontGlyph(codepoint: number): boolean {
|
||||
return 0xE000 <= codepoint && codepoint <= 0xF8FF;
|
||||
}
|
||||
|
||||
function isBoxOrBlockGlyph(codepoint: number): boolean {
|
||||
return 0x2500 <= codepoint && codepoint <= 0x259F;
|
||||
}
|
||||
|
||||
export function isEmoji(codepoint: number): boolean {
|
||||
return (
|
||||
codepoint >= 0x1F600 && codepoint <= 0x1F64F || // Emoticons
|
||||
codepoint >= 0x1F300 && codepoint <= 0x1F5FF || // Misc Symbols and Pictographs
|
||||
codepoint >= 0x1F680 && codepoint <= 0x1F6FF || // Transport and Map
|
||||
codepoint >= 0x2600 && codepoint <= 0x26FF || // Misc symbols
|
||||
codepoint >= 0x2700 && codepoint <= 0x27BF || // Dingbats
|
||||
codepoint >= 0xFE00 && codepoint <= 0xFE0F || // Variation Selectors
|
||||
codepoint >= 0x1F900 && codepoint <= 0x1F9FF || // Supplemental Symbols and Pictographs
|
||||
codepoint >= 0x1F1E6 && codepoint <= 0x1F1FF
|
||||
);
|
||||
}
|
||||
|
||||
export function allowRescaling(codepoint: number | undefined, width: number, glyphSizeX: number, deviceCellWidth: number): boolean {
|
||||
return (
|
||||
// Is single cell width
|
||||
width === 1 &&
|
||||
// Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that
|
||||
// barely overlap
|
||||
glyphSizeX > Math.ceil(deviceCellWidth * 1.5) &&
|
||||
// Never rescale ascii
|
||||
codepoint !== undefined && codepoint > 0xFF &&
|
||||
// Never rescale emoji
|
||||
!isEmoji(codepoint) &&
|
||||
// Never rescale powerline or nerd fonts
|
||||
!isPowerlineGlyph(codepoint) && !isNerdFontGlyph(codepoint)
|
||||
);
|
||||
}
|
||||
|
||||
export function treatGlyphAsBackgroundColor(codepoint: number): boolean {
|
||||
return isPowerlineGlyph(codepoint) || isBoxOrBlockGlyph(codepoint);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,8 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
'fontSize',
|
||||
'fontWeight',
|
||||
'fontWeightBold',
|
||||
'minimumContrastRatio'
|
||||
'minimumContrastRatio',
|
||||
'rescaleOverlappingGlyphs'
|
||||
], () => {
|
||||
this.clear();
|
||||
this.handleResize(bufferService.cols, bufferService.rows);
|
||||
@@ -247,7 +248,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
return;
|
||||
}
|
||||
if (this._isPaused) {
|
||||
this._pausedResizeTask.set(() => this._renderer.value!.handleResize(cols, rows));
|
||||
this._pausedResizeTask.set(() => this._renderer.value?.handleResize(cols, rows));
|
||||
} else {
|
||||
this._renderer.value.handleResize(cols, rows);
|
||||
}
|
||||
|
||||
+5
-2
@@ -3,7 +3,6 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { isNode } from 'common/Platform';
|
||||
import { IColor, IColorRGB } from 'common/Types';
|
||||
|
||||
let $r = 0;
|
||||
@@ -117,9 +116,10 @@ export namespace color {
|
||||
* '#rrggbbaa').
|
||||
*/
|
||||
export namespace css {
|
||||
// Attempt to set get the shared canvas context
|
||||
let $ctx: CanvasRenderingContext2D | undefined;
|
||||
let $litmusColor: CanvasGradient | undefined;
|
||||
if (!isNode) {
|
||||
try {
|
||||
// This is guaranteed to run in the first window, so document should be correct
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 1;
|
||||
@@ -133,6 +133,9 @@ export namespace css {
|
||||
$litmusColor = $ctx.createLinearGradient(0, 0, 1, 1);
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// noop
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a css string to an IColor, this should handle all valid CSS color strings and will
|
||||
|
||||
@@ -168,6 +168,10 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
||||
this._writeBuffer.writeSync(data, maxSubsequentCalls);
|
||||
}
|
||||
|
||||
public input(data: string, wasUserInput: boolean = true): void {
|
||||
this.coreService.triggerDataEvent(data, wasUserInput);
|
||||
}
|
||||
|
||||
public resize(x: number, y: number): void {
|
||||
if (isNaN(x) || isNaN(y)) {
|
||||
return;
|
||||
|
||||
@@ -2979,7 +2979,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
if (args[1]) {
|
||||
return this._createHyperlink(args[0], args[1]);
|
||||
}
|
||||
if (args[0]) {
|
||||
if (args[0].trim()) {
|
||||
return false;
|
||||
}
|
||||
return this._finishHyperlink();
|
||||
|
||||
@@ -14,7 +14,7 @@ interface INavigator {
|
||||
declare const navigator: INavigator;
|
||||
declare const process: unknown;
|
||||
|
||||
export const isNode = (typeof process !== 'undefined') ? true : false;
|
||||
export const isNode = (typeof process !== 'undefined' && 'title' in (process as any)) ? true : false;
|
||||
const userAgent = (isNode) ? 'node' : navigator.userAgent;
|
||||
const platform = (isNode) ? 'node' : navigator.platform;
|
||||
|
||||
|
||||
+86
-10
@@ -3,16 +3,27 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IdleTaskQueue } from 'common/TaskQueue';
|
||||
|
||||
// Work variables to avoid garbage collection.
|
||||
let i = 0;
|
||||
|
||||
/**
|
||||
* A generic list that is maintained in sorted order and allows values with duplicate keys. This
|
||||
* list is based on binary search and as such locating a key will take O(log n) amortized, this
|
||||
* includes the by key iterator.
|
||||
* A generic list that is maintained in sorted order and allows values with duplicate keys. Deferred
|
||||
* batch insertion and deletion is used to significantly reduce the time it takes to insert and
|
||||
* delete a large amount of items in succession. This list is based on binary search and as such
|
||||
* locating a key will take O(log n) amortized, this includes the by key iterator.
|
||||
*/
|
||||
export class SortedList<T> {
|
||||
private readonly _array: T[] = [];
|
||||
private _array: T[] = [];
|
||||
|
||||
private readonly _insertedValues: T[] = [];
|
||||
private readonly _flushInsertedTask = new IdleTaskQueue();
|
||||
private _isFlushingInserted = false;
|
||||
|
||||
private readonly _deletedIndices: number[] = [];
|
||||
private readonly _flushDeletedTask = new IdleTaskQueue();
|
||||
private _isFlushingDeleted = false;
|
||||
|
||||
constructor(
|
||||
private readonly _getKey: (value: T) => number
|
||||
@@ -21,18 +32,50 @@ export class SortedList<T> {
|
||||
|
||||
public clear(): void {
|
||||
this._array.length = 0;
|
||||
this._insertedValues.length = 0;
|
||||
this._flushInsertedTask.clear();
|
||||
this._isFlushingInserted = false;
|
||||
this._deletedIndices.length = 0;
|
||||
this._flushDeletedTask.clear();
|
||||
this._isFlushingDeleted = false;
|
||||
}
|
||||
|
||||
public insert(value: T): void {
|
||||
if (this._array.length === 0) {
|
||||
this._array.push(value);
|
||||
return;
|
||||
this._flushCleanupDeleted();
|
||||
if (this._insertedValues.length === 0) {
|
||||
this._flushInsertedTask.enqueue(() => this._flushInserted());
|
||||
}
|
||||
this._insertedValues.push(value);
|
||||
}
|
||||
|
||||
private _flushInserted(): void {
|
||||
const sortedAddedValues = this._insertedValues.sort((a, b) => this._getKey(a) - this._getKey(b));
|
||||
let sortedAddedValuesIndex = 0;
|
||||
let arrayIndex = 0;
|
||||
|
||||
const newArray = new Array(this._array.length + this._insertedValues.length);
|
||||
|
||||
for (let newArrayIndex = 0; newArrayIndex < newArray.length; newArrayIndex++) {
|
||||
if (arrayIndex >= this._array.length || this._getKey(sortedAddedValues[sortedAddedValuesIndex]) <= this._getKey(this._array[arrayIndex])) {
|
||||
newArray[newArrayIndex] = sortedAddedValues[sortedAddedValuesIndex];
|
||||
sortedAddedValuesIndex++;
|
||||
} else {
|
||||
newArray[newArrayIndex] = this._array[arrayIndex++];
|
||||
}
|
||||
}
|
||||
|
||||
this._array = newArray;
|
||||
this._insertedValues.length = 0;
|
||||
}
|
||||
|
||||
private _flushCleanupInserted(): void {
|
||||
if (!this._isFlushingInserted && this._insertedValues.length > 0) {
|
||||
this._flushInsertedTask.flush();
|
||||
}
|
||||
i = this._search(this._getKey(value));
|
||||
this._array.splice(i, 0, value);
|
||||
}
|
||||
|
||||
public delete(value: T): boolean {
|
||||
this._flushCleanupInserted();
|
||||
if (this._array.length === 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -49,14 +92,43 @@ export class SortedList<T> {
|
||||
}
|
||||
do {
|
||||
if (this._array[i] === value) {
|
||||
this._array.splice(i, 1);
|
||||
if (this._deletedIndices.length === 0) {
|
||||
this._flushDeletedTask.enqueue(() => this._flushDeleted());
|
||||
}
|
||||
this._deletedIndices.push(i);
|
||||
return true;
|
||||
}
|
||||
} while (++i < this._array.length && this._getKey(this._array[i]) === key);
|
||||
return false;
|
||||
}
|
||||
|
||||
private _flushDeleted(): void {
|
||||
this._isFlushingDeleted = true;
|
||||
const sortedDeletedIndices = this._deletedIndices.sort((a, b) => a - b);
|
||||
let sortedDeletedIndicesIndex = 0;
|
||||
const newArray = new Array(this._array.length - sortedDeletedIndices.length);
|
||||
let newArrayIndex = 0;
|
||||
for (let i = 0; i < this._array.length; i++) {
|
||||
if (sortedDeletedIndices[sortedDeletedIndicesIndex] === i) {
|
||||
sortedDeletedIndicesIndex++;
|
||||
} else {
|
||||
newArray[newArrayIndex++] = this._array[i];
|
||||
}
|
||||
}
|
||||
this._array = newArray;
|
||||
this._deletedIndices.length = 0;
|
||||
this._isFlushingDeleted = false;
|
||||
}
|
||||
|
||||
private _flushCleanupDeleted(): void {
|
||||
if (!this._isFlushingDeleted && this._deletedIndices.length > 0) {
|
||||
this._flushDeletedTask.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public *getKeyIterator(key: number): IterableIterator<T> {
|
||||
this._flushCleanupInserted();
|
||||
this._flushCleanupDeleted();
|
||||
if (this._array.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -73,6 +145,8 @@ export class SortedList<T> {
|
||||
}
|
||||
|
||||
public forEachByKey(key: number, callback: (value: T) => void): void {
|
||||
this._flushCleanupInserted();
|
||||
this._flushCleanupDeleted();
|
||||
if (this._array.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -89,6 +163,8 @@ export class SortedList<T> {
|
||||
}
|
||||
|
||||
public values(): IterableIterator<T> {
|
||||
this._flushCleanupInserted();
|
||||
this._flushCleanupDeleted();
|
||||
// Duplicate the array to avoid issues when _array changes while iterating
|
||||
return [...this._array].values();
|
||||
}
|
||||
|
||||
@@ -45,7 +45,8 @@ export class DecorationService extends Disposable implements IDecorationService
|
||||
const decoration = new Decoration(options);
|
||||
if (decoration) {
|
||||
const markerDispose = decoration.marker.onDispose(() => decoration.dispose());
|
||||
decoration.onDispose(() => {
|
||||
const listener = decoration.onDispose(() => {
|
||||
listener.dispose();
|
||||
if (decoration) {
|
||||
if (this._decorations.delete(decoration)) {
|
||||
this._onDecorationRemoved.fire(decoration);
|
||||
|
||||
@@ -44,6 +44,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
|
||||
allowTransparency: false,
|
||||
tabStopWidth: 8,
|
||||
theme: {},
|
||||
rescaleOverlappingGlyphs: false,
|
||||
rightClickSelectsWord: isMac,
|
||||
windowOptions: {},
|
||||
windowsMode: false,
|
||||
|
||||
@@ -234,6 +234,7 @@ export interface ITerminalOptions {
|
||||
macOptionIsMeta?: boolean;
|
||||
macOptionClickForcesSelection?: boolean;
|
||||
minimumContrastRatio?: number;
|
||||
rescaleOverlappingGlyphs?: boolean;
|
||||
rightClickSelectsWord?: boolean;
|
||||
rows?: number;
|
||||
screenReaderMode?: boolean;
|
||||
|
||||
@@ -81,6 +81,10 @@ export class Terminal extends CoreTerminal {
|
||||
this._onBell.fire();
|
||||
}
|
||||
|
||||
public input(data: string, wasUserInput: boolean = true): void {
|
||||
this.coreService.triggerDataEvent(data, wasUserInput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the terminal.
|
||||
*
|
||||
|
||||
@@ -80,6 +80,7 @@ export class Terminal extends Disposable implements ITerminalApi {
|
||||
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
|
||||
public get onScroll(): IEvent<number> { return this._core.onScroll; }
|
||||
public get onTitleChange(): IEvent<string> { return this._core.onTitleChange; }
|
||||
public get onWriteParsed(): IEvent<void> { return this._core.onWriteParsed; }
|
||||
|
||||
public get parser(): IParser {
|
||||
this._checkProposedApi();
|
||||
@@ -134,6 +135,9 @@ export class Terminal extends Disposable implements ITerminalApi {
|
||||
this._publicOptions[propName] = options[propName];
|
||||
}
|
||||
}
|
||||
public input(data: string, wasUserInput: boolean = true): void {
|
||||
this._core.input(data, wasUserInput);
|
||||
}
|
||||
public resize(columns: number, rows: number): void {
|
||||
this._verifyIntegers(columns, rows);
|
||||
this._core.resize(columns, rows);
|
||||
|
||||
@@ -989,13 +989,15 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void
|
||||
};
|
||||
await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
|
||||
await ctx.value.proxy.focus();
|
||||
await ctx.value.proxy.writeln('\x1b[41m red bg');
|
||||
await ctx.value.proxy.writeln('\x1b[7m inverse');
|
||||
await ctx.value.proxy.writeln('\x1b[31;7m red fg inverse');
|
||||
await ctx.value.proxy.writeln('\x1b[41m red bg\x1b[0m');
|
||||
await ctx.value.proxy.writeln('\x1b[7m inverse\x1b[0m');
|
||||
await ctx.value.proxy.writeln('\x1b[31;7m red fg inverse\x1b[0m');
|
||||
await ctx.value.proxy.writeln('\x1b[48:2:0:204:0:0m red truecolor bg\x1b[0m');
|
||||
await ctx.value.proxy.selectAll();
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [230,128,128,255]);
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [255,255,255,255]);
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 3), [230,128,128,255]);
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [230, 128, 128, 255]);
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [255, 255, 255, 255]);
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 3), [230, 128, 128, 255]);
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 4), [230, 128, 128, 255]);
|
||||
});
|
||||
test('powerline decorative symbols', async () => {
|
||||
const theme: ITheme = {
|
||||
@@ -1232,6 +1234,20 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, rows), [0, 0, 0, 255]);
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, rows, CellColorPosition.FIRST), [0, 0, 255, 255]);
|
||||
});
|
||||
test('#4917 The selection should not be displayed if it is not within the scope of the viewport.', async () => {
|
||||
const theme: ITheme = {
|
||||
selectionBackground: '#FF0000'
|
||||
};
|
||||
await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
|
||||
for (let index = 0; index < 160; index++) {
|
||||
await ctx.value.proxy.writeln(``);
|
||||
}
|
||||
await ctx.value.proxy.scrollToBottom();
|
||||
const rows = await ctx.value.proxy.buffer.active.length;
|
||||
await ctx.value.proxy.selectLines(rows - 1, rows - 1);
|
||||
await ctx.value.proxy.scrollLines(-2);
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -216,6 +216,7 @@ export class TerminalProxy implements ITerminalProxyCustomMethods, PlaywrightApi
|
||||
return new Promise(r => term.writeln(typeof data === 'string' ? data : new Uint8Array(data), r));
|
||||
}, [await this.getHandle(), typeof data === 'string' ? data : Array.from(data)] as const);
|
||||
}
|
||||
public async input(data: string, wasUserInput: boolean = true): Promise<void> { return this.evaluate(([term]) => term.input(data, wasUserInput)); }
|
||||
public async resize(cols: number, rows: number): Promise<void> { return this._page.evaluate(([term, cols, rows]) => term.resize(cols, rows), [await this.getHandle(), cols, rows] as const); }
|
||||
public async registerMarker(y?: number | undefined): Promise<IMarker> { return this._page.evaluate(([term, y]) => term.registerMarker(y), [await this.getHandle(), y] as const); }
|
||||
public async registerDecoration(decorationOptions: IDecorationOptions): Promise<IDecoration | undefined> { return this._page.evaluate(([term, decorationOptions]) => term.registerDecoration(decorationOptions), [await this.getHandle(), decorationOptions] as const); }
|
||||
|
||||
Vendored
+41
-1
@@ -140,6 +140,23 @@ declare module '@xterm/headless' {
|
||||
*/
|
||||
minimumContrastRatio?: number;
|
||||
|
||||
/**
|
||||
* Whether to rescale glyphs horizontally that are a single cell wide but
|
||||
* have glyphs that would overlap following cell(s). This typically happens
|
||||
* for ambiguous width characters (eg. the roman numeral characters U+2160+)
|
||||
* which aren't featured in monospace fonts. This is an important feature
|
||||
* for achieving GB18030 compliance.
|
||||
*
|
||||
* The following glyphs will never be rescaled:
|
||||
*
|
||||
* - Emoji glyphs
|
||||
* - Powerline glyphs
|
||||
* - Nerd font glyphs
|
||||
*
|
||||
* Note that this doesn't work with the DOM renderer. The default is false.
|
||||
*/
|
||||
rescaleOverlappingGlyphs?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to select the word under the cursor on right click, this is
|
||||
* standard behavior in a lot of macOS applications.
|
||||
@@ -156,7 +173,7 @@ declare module '@xterm/headless' {
|
||||
/**
|
||||
* The amount of scrollback in the terminal. Scrollback is the amount of
|
||||
* rows that are retained when lines are scrolled beyond the initial
|
||||
* viewport.
|
||||
* viewport. Defaults to 1000.
|
||||
*/
|
||||
scrollback?: number;
|
||||
|
||||
@@ -697,6 +714,17 @@ declare module '@xterm/headless' {
|
||||
*/
|
||||
onLineFeed: IEvent<void>;
|
||||
|
||||
/**
|
||||
* Adds an event listener for when data has been parsed by the terminal,
|
||||
* after {@link write} is called. This event is useful to listen for any
|
||||
* changes in the buffer.
|
||||
*
|
||||
* This fires at most once per frame, after data parsing completes. Note
|
||||
* that this can fire when there are still writes pending if there is a lot
|
||||
* of data.
|
||||
*/
|
||||
onWriteParsed: IEvent<void>;
|
||||
|
||||
/**
|
||||
* Adds an event listener for when the terminal is resized. The event value
|
||||
* contains the new size.
|
||||
@@ -718,6 +746,18 @@ declare module '@xterm/headless' {
|
||||
*/
|
||||
onTitleChange: IEvent<string>;
|
||||
|
||||
/**
|
||||
* Input data to application side. The data is treated the same way input
|
||||
* typed into the terminal would (ie. the {@link onData} event will fire).
|
||||
* @param data The data to forward to the application.
|
||||
* @param wasUserInput Whether the input is genuine user input. This is true
|
||||
* by default and triggers additionalbehavior like focus or selection
|
||||
* clearing. Set this to false if the data sent should not be treated like
|
||||
* user input would, for example passing an escape sequence to the
|
||||
* application.
|
||||
*/
|
||||
input(data: string, wasUserInput?: boolean): void;
|
||||
|
||||
/**
|
||||
* Resizes the terminal. It's best practice to debounce calls to resize,
|
||||
* this will help ensure that the pty can respond to the resize event
|
||||
|
||||
Vendored
+35
-4
@@ -47,11 +47,13 @@ declare module '@xterm/xterm' {
|
||||
|
||||
/**
|
||||
* When enabled the cursor will be set to the beginning of the next line
|
||||
* with every new line. This is equivalent to sending '\r\n' for each '\n'.
|
||||
* Normally the termios settings of the underlying PTY deals with the
|
||||
* translation of '\n' to '\r\n' and this setting should not be used. If you
|
||||
* with every new line. This is equivalent to sending `\r\n` for each `\n`.
|
||||
* Normally the settings of the underlying PTY (`termios`) deal with the
|
||||
* translation of `\n` to `\r\n` and this setting should not be used. If you
|
||||
* deal with data from a non-PTY related source, this settings might be
|
||||
* useful.
|
||||
*
|
||||
* @see https://pubs.opengroup.org/onlinepubs/007904975/basedefs/termios.h.html
|
||||
*/
|
||||
convertEol?: boolean;
|
||||
|
||||
@@ -209,6 +211,23 @@ declare module '@xterm/xterm' {
|
||||
*/
|
||||
minimumContrastRatio?: number;
|
||||
|
||||
/**
|
||||
* Whether to rescale glyphs horizontally that are a single cell wide but
|
||||
* have glyphs that would overlap following cell(s). This typically happens
|
||||
* for ambiguous width characters (eg. the roman numeral characters U+2160+)
|
||||
* which aren't featured in monospace fonts. This is an important feature
|
||||
* for achieving GB18030 compliance.
|
||||
*
|
||||
* The following glyphs will never be rescaled:
|
||||
*
|
||||
* - Emoji glyphs
|
||||
* - Powerline glyphs
|
||||
* - Nerd font glyphs
|
||||
*
|
||||
* Note that this doesn't work with the DOM renderer. The default is false.
|
||||
*/
|
||||
rescaleOverlappingGlyphs?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to select the word under the cursor on right click, this is
|
||||
* standard behavior in a lot of macOS applications.
|
||||
@@ -225,7 +244,7 @@ declare module '@xterm/xterm' {
|
||||
/**
|
||||
* The amount of scrollback in the terminal. Scrollback is the amount of
|
||||
* rows that are retained when lines are scrolled beyond the initial
|
||||
* viewport.
|
||||
* viewport. Defaults to 1000.
|
||||
*/
|
||||
scrollback?: number;
|
||||
|
||||
@@ -963,6 +982,18 @@ declare module '@xterm/xterm' {
|
||||
*/
|
||||
focus(): void;
|
||||
|
||||
/**
|
||||
* Input data to application side. The data is treated the same way input
|
||||
* typed into the terminal would (ie. the {@link onData} event will fire).
|
||||
* @param data The data to forward to the application.
|
||||
* @param wasUserInput Whether the input is genuine user input. This is true
|
||||
* by default and triggers additionalbehavior like focus or selection
|
||||
* clearing. Set this to false if the data sent should not be treated like
|
||||
* user input would, for example passing an escape sequence to the
|
||||
* application.
|
||||
*/
|
||||
input(data: string, wasUserInput?: boolean): void;
|
||||
|
||||
/**
|
||||
* Resizes the terminal. It's best practice to debounce calls to resize,
|
||||
* this will help ensure that the pty can respond to the resize event
|
||||
|
||||
@@ -39,8 +39,10 @@ const config = {
|
||||
path: path.resolve('./headless/lib-headless'),
|
||||
library: {
|
||||
type: 'commonjs'
|
||||
}
|
||||
},
|
||||
// Force usage of globalThis instead of global / self. (This is cross-env compatible)
|
||||
globalObject: 'globalThis',
|
||||
},
|
||||
mode: 'production'
|
||||
mode: 'production',
|
||||
};
|
||||
module.exports = config;
|
||||
|
||||
@@ -1099,13 +1099,13 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
body-parser@1.20.1:
|
||||
version "1.20.1"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
|
||||
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
|
||||
body-parser@1.20.2:
|
||||
version "1.20.2"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
|
||||
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
content-type "~1.0.4"
|
||||
content-type "~1.0.5"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
destroy "1.2.0"
|
||||
@@ -1113,7 +1113,7 @@ body-parser@1.20.1:
|
||||
iconv-lite "0.4.24"
|
||||
on-finished "2.4.1"
|
||||
qs "6.11.0"
|
||||
raw-body "2.5.1"
|
||||
raw-body "2.5.2"
|
||||
type-is "~1.6.18"
|
||||
unpipe "1.0.0"
|
||||
|
||||
@@ -1402,7 +1402,7 @@ content-disposition@0.5.4:
|
||||
dependencies:
|
||||
safe-buffer "5.2.1"
|
||||
|
||||
content-type@~1.0.4:
|
||||
content-type@~1.0.4, content-type@~1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
|
||||
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
|
||||
@@ -1417,10 +1417,10 @@ cookie-signature@1.0.6:
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
|
||||
|
||||
cookie@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
cookie@0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
|
||||
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
|
||||
|
||||
cross-env@^7.0.3:
|
||||
version "7.0.3"
|
||||
@@ -1832,16 +1832,16 @@ express-ws@^5.0.2:
|
||||
ws "^7.4.6"
|
||||
|
||||
express@^4.17.1:
|
||||
version "4.18.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
|
||||
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
|
||||
version "4.19.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
|
||||
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
|
||||
dependencies:
|
||||
accepts "~1.3.8"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.20.1"
|
||||
body-parser "1.20.2"
|
||||
content-disposition "0.5.4"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.5.0"
|
||||
cookie "0.6.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
@@ -3242,10 +3242,10 @@ range-parser@~1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
||||
|
||||
raw-body@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
|
||||
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
|
||||
raw-body@2.5.2:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
|
||||
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
http-errors "2.0.0"
|
||||
|
||||
Reference in New Issue
Block a user