Compare commits
87
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac923e55d2 | ||
|
|
e08e5607ed | ||
|
|
2ac2eeadfc | ||
|
|
ffadc0b31d | ||
|
|
cc1089e943 | ||
|
|
47c21df83d | ||
|
|
380e6805e8 | ||
|
|
9f424833d6 | ||
|
|
90ccc815cb | ||
|
|
8de7bd5850 | ||
|
|
9fed8e2a3a | ||
|
|
118915c1f7 | ||
|
|
fca593d93b | ||
|
|
3376ee2f85 | ||
|
|
f6fbf63673 | ||
|
|
3e5c6a156b | ||
|
|
a91665cb18 | ||
|
|
22140b5e94 | ||
|
|
22a4dc4b6b | ||
|
|
0ff06db417 | ||
|
|
83a77b6024 | ||
|
|
8b422442b7 | ||
|
|
02ce3eff87 | ||
|
|
518b87f01d | ||
|
|
6188c07bf7 | ||
|
|
325d133a75 | ||
|
|
b662824b81 | ||
|
|
ada5bfe048 | ||
|
|
f270127ce5 | ||
|
|
7460782494 | ||
|
|
048010c219 | ||
|
|
cdec1e5b03 | ||
|
|
eb56356133 | ||
|
|
2bfd95fe54 | ||
|
|
e9b0593298 | ||
|
|
d5c47ec531 | ||
|
|
5a145fcffe | ||
|
|
539fed4daa | ||
|
|
a0493a604c | ||
|
|
07e8f86bd1 | ||
|
|
1dbc1bf639 | ||
|
|
6e8c59f0db | ||
|
|
ec227c757b | ||
|
|
4fbeb58203 | ||
|
|
60f549028f | ||
|
|
cc5136aeb3 | ||
|
|
a1e9105ce7 | ||
|
|
7a7f4f0add | ||
|
|
96ea9fe188 | ||
|
|
b4e2293dbc | ||
|
|
7194223696 | ||
|
|
8c9e44c219 | ||
|
|
075f33031d | ||
|
|
e4b670231d | ||
|
|
1c19f1d282 | ||
|
|
508589aa80 | ||
|
|
854e2736f6 | ||
|
|
58f650e6f0 | ||
|
|
913cb25863 | ||
|
|
16f82cb3b5 | ||
|
|
0a101a01f5 | ||
|
|
9638063359 | ||
|
|
ee26780e03 | ||
|
|
b35415ed16 | ||
|
|
2a9ec8fd1d | ||
|
|
2fdb46919c | ||
|
|
c8e633ecb5 | ||
|
|
c546f7cc8a | ||
|
|
6997f1a940 | ||
|
|
14c6df3187 | ||
|
|
18be346df9 | ||
|
|
37297e09ed | ||
|
|
b2f0cc8dc1 | ||
|
|
6cbee3349e | ||
|
|
f0edc1b613 | ||
|
|
cd9867e222 | ||
|
|
e1d852c86a | ||
|
|
455c744ab1 | ||
|
|
21f48ba803 | ||
|
|
49fdacba6a | ||
|
|
a359c7ca10 | ||
|
|
242eade757 | ||
|
|
4037b943b9 | ||
|
|
9154831afa | ||
|
|
d6599bc048 | ||
|
|
8a1faed5cb | ||
|
|
72a157df5e |
@@ -1,23 +0,0 @@
|
||||
FROM node:14-buster
|
||||
|
||||
# Configure apt
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install --no-install-recommends apt-utils 2>&1
|
||||
|
||||
# Verify git and process tools are installed
|
||||
RUN apt-get install -y git procps
|
||||
|
||||
# Install yarn
|
||||
RUN apt-get install -y curl apt-transport-https lsb-release \
|
||||
&& curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \
|
||||
&& echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
|
||||
&& apt-get update \
|
||||
&& apt-get -y install --no-install-recommends \
|
||||
yarn
|
||||
|
||||
# Clean up
|
||||
RUN apt-get autoremove -y \
|
||||
&& apt-get clean -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
ENV DEBIAN_FRONTEND=dialog
|
||||
@@ -1,10 +1,20 @@
|
||||
{
|
||||
"name": "xterm.js",
|
||||
"dockerFile": "Dockerfile",
|
||||
"appPort": 3000,
|
||||
"extensions": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"editorconfig.editorconfig",
|
||||
"hbenl.vscode-mocha-test-adapter"
|
||||
]
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node:0-18-buster",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {} // yarn
|
||||
},
|
||||
"forwardPorts": [
|
||||
3000
|
||||
],
|
||||
"postCreateCommand": "yarn install",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"editorconfig.editorconfig",
|
||||
"hbenl.vscode-mocha-test-adapter"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: FAQ
|
||||
url: https://github.com/xtermjs/xterm.js/wiki/FAQ
|
||||
about: See our frequently asked questions before filing a bug report
|
||||
- name: Support / Q&A
|
||||
url: https://github.com/xtermjs/xterm.js/discussions/categories/q-a
|
||||
about: Use GitHub Discussions for community support and general Q&A
|
||||
|
||||
@@ -215,6 +215,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
||||
- [**Cratecode**](https://cratecode.com): Learn to program for free through interactive online lessons. Cratecode uses xterm.js to give users access to their own Linux environment.
|
||||
- [**Super Terminal**](https://github.com/bugwheels94/super-terminal): It is a http based terminal for developers who dont like repetition and save time.
|
||||
- [**graSSHopper**](https://grasshopper.coding.kiwi): A simple SSH client with file explorer, history and many more features.
|
||||
- [**DomTerm**](https://domterm.org/xtermjs.html): Tiles and tabs. Detachable sessions (like tmux). [Remote connections](https://domterm.org/Remoting-over-ssh.html) using a nice ssh wrapper with predictive echo. Qt, Electron, Tauri/Wry, or desktop browser front-ends. Choose between xterm.js engine (faster) or native DomTerm (more functionality and graphics) - or both.
|
||||
- [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-canvas",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -379,7 +379,17 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
|
||||
}
|
||||
this._ctx.save();
|
||||
this._clipRow(y);
|
||||
|
||||
// Draw the image, use the bitmap if it's available
|
||||
|
||||
// HACK: If the canvas doesn't match, delete the generator. It's not clear how this happens but
|
||||
// something is wrong with either the lifecycle of _bitmapGenerator or the page canvases are
|
||||
// swapped out unexpectedly
|
||||
if (this._bitmapGenerator[glyph.texturePage] && this._charAtlas.pages[glyph.texturePage].canvas !== this._bitmapGenerator[glyph.texturePage]!.canvas) {
|
||||
this._bitmapGenerator[glyph.texturePage]?.bitmap?.close();
|
||||
delete this._bitmapGenerator[glyph.texturePage];
|
||||
}
|
||||
|
||||
if (this._charAtlas.pages[glyph.texturePage].version !== this._bitmapGenerator[glyph.texturePage]?.version) {
|
||||
if (!this._bitmapGenerator[glyph.texturePage]) {
|
||||
this._bitmapGenerator[glyph.texturePage] = new BitmapGenerator(this._charAtlas.pages[glyph.texturePage].canvas);
|
||||
@@ -446,11 +456,12 @@ class BitmapGenerator {
|
||||
public get bitmap(): ImageBitmap | undefined { return this._bitmap; }
|
||||
public version: number = -1;
|
||||
|
||||
constructor(private readonly _canvas: HTMLCanvasElement) {
|
||||
constructor(public readonly canvas: HTMLCanvasElement) {
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
// Clear the bitmap immediately as it's stale
|
||||
this._bitmap?.close();
|
||||
this._bitmap = undefined;
|
||||
// Disable ImageBitmaps on Safari because of https://bugs.webkit.org/show_bug.cgi?id=149990
|
||||
if (isSafari) {
|
||||
@@ -466,9 +477,10 @@ class BitmapGenerator {
|
||||
|
||||
private _generate(): void {
|
||||
if (this._state === BitmapGeneratorState.IDLE) {
|
||||
this._bitmap?.close();
|
||||
this._bitmap = undefined;
|
||||
this._state = BitmapGeneratorState.GENERATING;
|
||||
window.createImageBitmap(this._canvas).then(bitmap => {
|
||||
window.createImageBitmap(this.canvas).then(bitmap => {
|
||||
if (this._state === BitmapGeneratorState.GENERATING_INVALID) {
|
||||
this.refresh();
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
||||
import { IColorSet, ITerminal } from 'browser/Types';
|
||||
import { ITerminal } from 'browser/Types';
|
||||
import { CanvasRenderer } from './CanvasRenderer';
|
||||
import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
import { ITerminalAddon, Terminal } from 'xterm';
|
||||
|
||||
@@ -13,6 +13,7 @@ import { IEventEmitter } from 'common/EventEmitter';
|
||||
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
||||
import { Terminal } from 'xterm';
|
||||
import { toDisposable } from 'common/Lifecycle';
|
||||
import { isFirefox } from 'common/Platform';
|
||||
|
||||
interface ICursorState {
|
||||
x: number;
|
||||
@@ -149,11 +150,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this._themeService.colors.cursor.css;
|
||||
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
|
||||
if (cursorStyle && cursorStyle !== 'block') {
|
||||
this._cursorRenderers[cursorStyle](cursorX, viewportRelativeCursorY, this._cell);
|
||||
} else {
|
||||
this._renderBlurCursor(cursorX, viewportRelativeCursorY, this._cell);
|
||||
}
|
||||
this._renderBlurCursor(cursorX, viewportRelativeCursorY, this._cell);
|
||||
this._ctx.restore();
|
||||
this._state.x = cursorX;
|
||||
this._state.y = viewportRelativeCursorY;
|
||||
@@ -194,8 +191,9 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
|
||||
private _clearCursor(): void {
|
||||
if (this._state) {
|
||||
// Avoid potential rounding errors when device pixel ratio is less than 1
|
||||
if (this._coreBrowserService.dpr < 1) {
|
||||
// Avoid potential rounding errors when browser is Firefox (#4487) or device pixel ratio is
|
||||
// less than 1
|
||||
if (isFirefox || this._coreBrowserService.dpr < 1) {
|
||||
this._clearAll();
|
||||
} else {
|
||||
this._clearCells(this._state.x, this._state.y, this._state.width, 1);
|
||||
|
||||
@@ -96,10 +96,11 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
}
|
||||
|
||||
// exit early for NULL and SP
|
||||
const code = cell.getCode();
|
||||
if (code === 0 || code === 32) {
|
||||
continue;
|
||||
}
|
||||
// NOTE: commented out due to #4120 (needs a more clever patch to keep things performant)
|
||||
// const code = cell.getCode();
|
||||
// if (code === 0 || code === 32) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Process any joined character ranges as needed. Because of how the
|
||||
// ranges are produced, we know that they are valid for the characters
|
||||
@@ -187,12 +188,6 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
nextFillStyle = this._themeService.colors.ansi[cell.getBgColor()].css;
|
||||
}
|
||||
|
||||
// Apply dim to the background, this is relatively slow as the CSS is re-parsed but dim is
|
||||
// rarely used
|
||||
if (nextFillStyle && cell.isDim()) {
|
||||
nextFillStyle = color.multiplyOpacity(css.toColor(nextFillStyle), 0.5).css;
|
||||
}
|
||||
|
||||
// Get any decoration foreground/background overrides, this must be fetched before the early
|
||||
// exist but applied after inverse
|
||||
let isTop = false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-search",
|
||||
"version": "0.11.0",
|
||||
"version": "0.12.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Terminal, IDisposable, ITerminalAddon, IBufferRange, IDecoration } from 'xterm';
|
||||
import { Terminal, IDisposable, ITerminalAddon, IDecoration } from 'xterm';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { Disposable, toDisposable, disposeArray } from 'common/Lifecycle';
|
||||
|
||||
export interface ISearchOptions {
|
||||
regex?: boolean;
|
||||
@@ -30,6 +30,10 @@ export interface ISearchPosition {
|
||||
startRow: number;
|
||||
}
|
||||
|
||||
export interface ISearchAddonOptions {
|
||||
highlightLimit: number;
|
||||
}
|
||||
|
||||
export interface ISearchResult {
|
||||
term: string;
|
||||
col: number;
|
||||
@@ -48,15 +52,22 @@ type LineCacheEntry = [
|
||||
lineOffsets: number[]
|
||||
];
|
||||
|
||||
interface IHighlight extends IDisposable {
|
||||
decoration: IDecoration;
|
||||
match: ISearchResult;
|
||||
}
|
||||
|
||||
const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?';
|
||||
const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
|
||||
const DEFAULT_HIGHLIGHT_LIMIT = 1000;
|
||||
|
||||
export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
private _terminal: Terminal | undefined;
|
||||
private _cachedSearchTerm: string | undefined;
|
||||
private _selectedDecoration: IDecoration | undefined;
|
||||
private _resultDecorations: Map<number, IDecoration[]> | undefined;
|
||||
private _searchResults: Map<string, ISearchResult> | undefined;
|
||||
private _highlightedLines: Set<number> = new Set();
|
||||
private _highlightDecorations: IHighlight[] = [];
|
||||
private _selectedDecoration: IHighlight | undefined;
|
||||
private _highlightLimit: number;
|
||||
private _onDataDisposable: IDisposable | undefined;
|
||||
private _onResizeDisposable: IDisposable | undefined;
|
||||
private _lastSearchOptions: ISearchOptions | undefined;
|
||||
@@ -71,11 +82,15 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
private _cursorMoveListener: IDisposable | undefined;
|
||||
private _resizeListener: IDisposable | undefined;
|
||||
|
||||
private _resultIndex: number | undefined;
|
||||
|
||||
private readonly _onDidChangeResults = this.register(new EventEmitter<{ resultIndex: number, resultCount: number } | undefined>());
|
||||
private readonly _onDidChangeResults = this.register(new EventEmitter<{ resultIndex: number, resultCount: number }>());
|
||||
public readonly onDidChangeResults = this._onDidChangeResults.event;
|
||||
|
||||
constructor(options?: Partial<ISearchAddonOptions>) {
|
||||
super();
|
||||
|
||||
this._highlightLimit = options?.highlightLimit ?? DEFAULT_HIGHLIGHT_LIMIT;
|
||||
}
|
||||
|
||||
public activate(terminal: Terminal): void {
|
||||
this._terminal = terminal;
|
||||
this._onDataDisposable = this.register(this._terminal.onWriteParsed(() => this._updateMatches()));
|
||||
@@ -93,24 +108,18 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
}
|
||||
if (this._cachedSearchTerm && this._lastSearchOptions?.decorations) {
|
||||
this._highlightTimeout = setTimeout(() => {
|
||||
this.findPrevious(this._cachedSearchTerm!, { ...this._lastSearchOptions, incremental: true, noScroll: true });
|
||||
this._resultIndex = this._searchResults ? this._searchResults.size - 1 : -1;
|
||||
this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults?.size ?? -1 });
|
||||
const term = this._cachedSearchTerm;
|
||||
this._cachedSearchTerm = undefined;
|
||||
this.findPrevious(term!, { ...this._lastSearchOptions, incremental: true, noScroll: true });
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
public clearDecorations(retainCachedSearchTerm?: boolean): void {
|
||||
this._selectedDecoration?.dispose();
|
||||
this._searchResults?.clear();
|
||||
this._resultDecorations?.forEach(decorations => {
|
||||
for (const d of decorations) {
|
||||
d.dispose();
|
||||
}
|
||||
});
|
||||
this._resultDecorations?.clear();
|
||||
this._searchResults = undefined;
|
||||
this._resultDecorations = undefined;
|
||||
this.clearActiveDecoration();
|
||||
disposeArray(this._highlightDecorations);
|
||||
this._highlightDecorations = [];
|
||||
this._highlightedLines.clear();
|
||||
if (!retainCachedSearchTerm) {
|
||||
this._cachedSearchTerm = undefined;
|
||||
}
|
||||
@@ -134,11 +143,16 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
}
|
||||
this._lastSearchOptions = searchOptions;
|
||||
if (searchOptions?.decorations) {
|
||||
if (this._resultIndex !== undefined || this._cachedSearchTerm === undefined || term !== this._cachedSearchTerm) {
|
||||
if (this._cachedSearchTerm === undefined || term !== this._cachedSearchTerm) {
|
||||
this._highlightAllMatches(term, searchOptions);
|
||||
}
|
||||
}
|
||||
return this._fireResults(term, this._findNextAndSelect(term, searchOptions), searchOptions);
|
||||
|
||||
const found = this._findNextAndSelect(term, searchOptions);
|
||||
this._fireResults(searchOptions);
|
||||
this._cachedSearchTerm = term;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private _highlightAllMatches(term: string, searchOptions: ISearchOptions): void {
|
||||
@@ -153,32 +167,30 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
|
||||
// new search, clear out the old decorations
|
||||
this.clearDecorations(true);
|
||||
this._searchResults = new Map<string, ISearchResult>();
|
||||
this._resultDecorations = new Map<number, IDecoration[]>();
|
||||
const resultDecorations = this._resultDecorations;
|
||||
|
||||
const searchResultsWithHighlight: ISearchResult[] = [];
|
||||
let prevResult: ISearchResult | undefined = undefined;
|
||||
let result = this._find(term, 0, 0, searchOptions);
|
||||
while (result && !this._searchResults.get(`${result.row}-${result.col}`)) {
|
||||
this._searchResults.set(`${result.row}-${result.col}`, result);
|
||||
while (result && (prevResult?.row !== result.row || prevResult?.col !== result.col)) {
|
||||
if (searchResultsWithHighlight.length >= this._highlightLimit) {
|
||||
break;
|
||||
}
|
||||
prevResult = result;
|
||||
searchResultsWithHighlight.push(prevResult);
|
||||
result = this._find(
|
||||
term,
|
||||
result.col + result.term.length >= this._terminal.cols ? result.row + 1 : result.row,
|
||||
result.col + result.term.length >= this._terminal.cols ? 0 : result.col + 1,
|
||||
prevResult.col + prevResult.term.length >= this._terminal.cols ? prevResult.row + 1 : prevResult.row,
|
||||
prevResult.col + prevResult.term.length >= this._terminal.cols ? 0 : prevResult.col + 1,
|
||||
searchOptions
|
||||
);
|
||||
if (this._searchResults.size > 1000) {
|
||||
this.clearDecorations();
|
||||
this._resultIndex = undefined;
|
||||
return;
|
||||
}
|
||||
for (const match of searchResultsWithHighlight) {
|
||||
const decoration = this._createResultDecoration(match, searchOptions.decorations!);
|
||||
if (decoration) {
|
||||
this._highlightedLines.add(decoration.marker.line);
|
||||
this._highlightDecorations.push({ decoration, match, dispose() { decoration.dispose(); } });
|
||||
}
|
||||
}
|
||||
this._searchResults.forEach(result => {
|
||||
const resultDecoration = this._createResultDecoration(result, searchOptions.decorations!);
|
||||
if (resultDecoration) {
|
||||
const decorationsForLine = resultDecorations.get(resultDecoration.marker.line) || [];
|
||||
decorationsForLine.push(resultDecoration);
|
||||
resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _find(term: string, startRow: number, startCol: number, searchOptions?: ISearchOptions): ISearchResult | undefined {
|
||||
@@ -223,26 +235,22 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
if (!this._terminal || !term || term.length === 0) {
|
||||
this._terminal?.clearSelection();
|
||||
this.clearDecorations();
|
||||
this._cachedSearchTerm = undefined;
|
||||
this._resultIndex = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._cachedSearchTerm !== term) {
|
||||
this._resultIndex = undefined;
|
||||
this._terminal.clearSelection();
|
||||
}
|
||||
const prevSelectedPos = this._terminal.getSelectionPosition();
|
||||
this._terminal.clearSelection();
|
||||
|
||||
let startCol = 0;
|
||||
let startRow = 0;
|
||||
let currentSelection: IBufferRange | undefined;
|
||||
if (this._terminal.hasSelection()) {
|
||||
const incremental = searchOptions ? searchOptions.incremental : false;
|
||||
// Start from the selection end if there is a selection
|
||||
// For incremental search, use existing row
|
||||
currentSelection = this._terminal.getSelectionPosition()!;
|
||||
startRow = incremental ? currentSelection.start.y : currentSelection.end.y;
|
||||
startCol = incremental ? currentSelection.start.x : currentSelection.end.x;
|
||||
if (prevSelectedPos) {
|
||||
if (this._cachedSearchTerm === term) {
|
||||
startCol = prevSelectedPos.end.x;
|
||||
startRow = prevSelectedPos.end.y;
|
||||
} else {
|
||||
startCol = prevSelectedPos.start.x;
|
||||
startRow = prevSelectedPos.start.y;
|
||||
}
|
||||
}
|
||||
|
||||
this._initLinesCache();
|
||||
@@ -281,24 +289,12 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
}
|
||||
|
||||
// If there is only one result, wrap back and return selection if it exists.
|
||||
if (!result && currentSelection) {
|
||||
searchPosition.startRow = currentSelection.start.y;
|
||||
if (!result && prevSelectedPos) {
|
||||
searchPosition.startRow = prevSelectedPos.start.y;
|
||||
searchPosition.startCol = 0;
|
||||
result = this._findInLine(term, searchPosition, searchOptions);
|
||||
}
|
||||
|
||||
if (this._searchResults) {
|
||||
if (this._searchResults.size === 0) {
|
||||
this._resultIndex = -1;
|
||||
} else if (this._resultIndex === undefined) {
|
||||
this._resultIndex = 0;
|
||||
} else {
|
||||
this._resultIndex++;
|
||||
if (this._resultIndex >= this._searchResults.size) {
|
||||
this._resultIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set selection and scroll if a result was found
|
||||
return this._selectResult(result, searchOptions?.decorations, searchOptions?.noScroll);
|
||||
}
|
||||
@@ -315,75 +311,74 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
}
|
||||
this._lastSearchOptions = searchOptions;
|
||||
if (searchOptions?.decorations) {
|
||||
if (this._resultIndex !== undefined || this._cachedSearchTerm === undefined || term !== this._cachedSearchTerm) {
|
||||
if (this._cachedSearchTerm === undefined || term !== this._cachedSearchTerm) {
|
||||
this._highlightAllMatches(term, searchOptions);
|
||||
}
|
||||
}
|
||||
return this._fireResults(term, this._findPreviousAndSelect(term, searchOptions), searchOptions);
|
||||
|
||||
const found = this._findPreviousAndSelect(term, searchOptions);
|
||||
this._fireResults(searchOptions);
|
||||
this._cachedSearchTerm = term;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private _fireResults(term: string, found: boolean, searchOptions?: ISearchOptions): boolean {
|
||||
private _fireResults(searchOptions?: ISearchOptions): void {
|
||||
if (searchOptions?.decorations) {
|
||||
if (this._resultIndex !== undefined && this._searchResults?.size !== undefined) {
|
||||
this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults.size });
|
||||
} else {
|
||||
this._onDidChangeResults.fire(undefined);
|
||||
let resultIndex = -1;
|
||||
if (this._selectedDecoration) {
|
||||
const selectedMatch = this._selectedDecoration.match;
|
||||
for (let i = 0; i < this._highlightDecorations.length; i++) {
|
||||
const match = this._highlightDecorations[i].match;
|
||||
if (match.row === selectedMatch.row && match.col === selectedMatch.col && match.size === selectedMatch.size) {
|
||||
resultIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._onDidChangeResults.fire({ resultIndex, resultCount: this._highlightDecorations.length });
|
||||
}
|
||||
this._cachedSearchTerm = term;
|
||||
return found;
|
||||
}
|
||||
|
||||
private _findPreviousAndSelect(term: string, searchOptions?: ISearchOptions): boolean {
|
||||
if (!this._terminal) {
|
||||
throw new Error('Cannot use addon until it has been loaded');
|
||||
}
|
||||
let result: ISearchResult | undefined;
|
||||
if (!this._terminal || !term || term.length === 0) {
|
||||
result = undefined;
|
||||
this._terminal?.clearSelection();
|
||||
this.clearDecorations();
|
||||
this._resultIndex = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._cachedSearchTerm !== term) {
|
||||
this._resultIndex = undefined;
|
||||
this._terminal.clearSelection();
|
||||
}
|
||||
const prevSelectedPos = this._terminal.getSelectionPosition();
|
||||
this._terminal.clearSelection();
|
||||
|
||||
let startRow = this._terminal.buffer.active.baseY + this._terminal.rows;
|
||||
let startRow = this._terminal.buffer.active.baseY + this._terminal.rows - 1;
|
||||
let startCol = this._terminal.cols;
|
||||
const isReverseSearch = true;
|
||||
|
||||
const incremental = searchOptions ? searchOptions.incremental : false;
|
||||
let currentSelection: IBufferRange | undefined;
|
||||
if (this._terminal.hasSelection()) {
|
||||
currentSelection = this._terminal.getSelectionPosition()!;
|
||||
// Start from selection start if there is a selection
|
||||
startRow = currentSelection.start.y;
|
||||
startCol = currentSelection.start.x;
|
||||
}
|
||||
|
||||
this._initLinesCache();
|
||||
const searchPosition: ISearchPosition = {
|
||||
startRow,
|
||||
startCol
|
||||
};
|
||||
|
||||
if (incremental) {
|
||||
// Try to expand selection to right first.
|
||||
result = this._findInLine(term, searchPosition, searchOptions, false);
|
||||
const isOldResultHighlighted = result && result.row === startRow && result.col === startCol;
|
||||
if (!isOldResultHighlighted) {
|
||||
// If selection was not able to be expanded to the right, then try reverse search
|
||||
if (currentSelection) {
|
||||
searchPosition.startRow = currentSelection.end.y;
|
||||
searchPosition.startCol = currentSelection.end.x;
|
||||
let result: ISearchResult | undefined;
|
||||
if (prevSelectedPos) {
|
||||
searchPosition.startRow = startRow = prevSelectedPos.start.y;
|
||||
searchPosition.startCol = startCol = prevSelectedPos.start.x;
|
||||
if (this._cachedSearchTerm !== term) {
|
||||
// Try to expand selection to right first.
|
||||
result = this._findInLine(term, searchPosition, searchOptions, false);
|
||||
if (!result) {
|
||||
// If selection was not able to be expanded to the right, then try reverse search
|
||||
searchPosition.startRow = startRow = prevSelectedPos.end.y;
|
||||
searchPosition.startCol = startCol = prevSelectedPos.end.x;
|
||||
}
|
||||
result = this._findInLine(term, searchPosition, searchOptions, true);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
|
||||
}
|
||||
|
||||
@@ -399,8 +394,8 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
}
|
||||
}
|
||||
// If we hit the top and didn't search from the very bottom wrap back down
|
||||
if (!result && startRow !== (this._terminal.buffer.active.baseY + this._terminal.rows)) {
|
||||
for (let y = (this._terminal.buffer.active.baseY + this._terminal.rows); y >= startRow; y--) {
|
||||
if (!result && startRow !== (this._terminal.buffer.active.baseY + this._terminal.rows - 1)) {
|
||||
for (let y = (this._terminal.buffer.active.baseY + this._terminal.rows - 1); y >= startRow; y--) {
|
||||
searchPosition.startRow = y;
|
||||
result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
|
||||
if (result) {
|
||||
@@ -409,22 +404,6 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
}
|
||||
}
|
||||
|
||||
if (this._searchResults) {
|
||||
if (this._searchResults.size === 0) {
|
||||
this._resultIndex = -1;
|
||||
} else if (this._resultIndex === undefined || this._resultIndex < 0) {
|
||||
this._resultIndex = this._searchResults.size - 1;
|
||||
} else {
|
||||
this._resultIndex--;
|
||||
if (this._resultIndex === -1) {
|
||||
this._resultIndex = this._searchResults.size - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is only one result, return true.
|
||||
if (!result && currentSelection) return true;
|
||||
|
||||
// Set selection and scroll if a result was found
|
||||
return this._selectResult(result, searchOptions?.decorations, searchOptions?.noScroll);
|
||||
}
|
||||
@@ -675,7 +654,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
if (options) {
|
||||
const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
|
||||
if (marker) {
|
||||
this._selectedDecoration = terminal.registerDecoration({
|
||||
const decoration = terminal.registerDecoration({
|
||||
marker,
|
||||
x: result.col,
|
||||
width: result.size,
|
||||
@@ -685,8 +664,13 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
color: options.activeMatchColorOverviewRuler
|
||||
}
|
||||
});
|
||||
this._selectedDecoration?.onRender((e) => this._applyStyles(e, options.activeMatchBorder, true));
|
||||
this._selectedDecoration?.onDispose(() => marker.dispose());
|
||||
if (decoration) {
|
||||
const disposables: IDisposable[] = [];
|
||||
disposables.push(marker);
|
||||
disposables.push(decoration.onRender((e) => this._applyStyles(e, options.activeMatchBorder, true)));
|
||||
disposables.push(decoration.onDispose(() => disposeArray(disposables)));
|
||||
this._selectedDecoration = { decoration, match: result, dispose() { decoration.dispose(); } };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,9 +693,6 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
* @returns
|
||||
*/
|
||||
private _applyStyles(element: HTMLElement, borderColor: string | undefined, isActiveResult: boolean): void {
|
||||
if (element.clientWidth <= 0) {
|
||||
return;
|
||||
}
|
||||
if (!element.classList.contains('xterm-find-result-decoration')) {
|
||||
element.classList.add('xterm-find-result-decoration');
|
||||
if (borderColor) {
|
||||
@@ -740,13 +721,17 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
x: result.col,
|
||||
width: result.size,
|
||||
backgroundColor: options.matchBackground,
|
||||
overviewRulerOptions: this._resultDecorations?.get(marker.line) ? undefined : {
|
||||
overviewRulerOptions: this._highlightedLines.has(marker.line) ? undefined : {
|
||||
color: options.matchOverviewRuler,
|
||||
position: 'center'
|
||||
}
|
||||
});
|
||||
findResultDecoration?.onRender((e) => this._applyStyles(e, options.matchBorder, false));
|
||||
findResultDecoration?.onDispose(() => marker.dispose());
|
||||
if (findResultDecoration) {
|
||||
const disposables: IDisposable[] = [];
|
||||
disposables.push(marker);
|
||||
disposables.push(findResultDecoration.onRender((e) => this._applyStyles(e, options.matchBorder, false)));
|
||||
disposables.push(findResultDecoration.onDispose(() => disposeArray(disposables)));
|
||||
}
|
||||
return findResultDecoration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ describe('Search Tests', function (): void {
|
||||
window.calls = [];
|
||||
window.search.onDidChangeResults(e => window.calls.push(e));
|
||||
`);
|
||||
await writeSync(page, 'abc aabc');
|
||||
await writeSync(page, 'd abc aabc d');
|
||||
assert.deepStrictEqual(await page.evaluate(`window.search.findNext('a', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 3, resultIndex: 0 }
|
||||
@@ -201,15 +201,64 @@ describe('Search Tests', function (): void {
|
||||
{ resultCount: 2, resultIndex: 0 },
|
||||
{ resultCount: 2, resultIndex: 1 }
|
||||
]);
|
||||
assert.deepStrictEqual(await page.evaluate(`window.search.findNext('d', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 3, resultIndex: 0 },
|
||||
{ resultCount: 2, resultIndex: 0 },
|
||||
{ resultCount: 2, resultIndex: 0 },
|
||||
{ resultCount: 2, resultIndex: 1 },
|
||||
{ resultCount: 2, resultIndex: 1 }
|
||||
]);
|
||||
assert.deepStrictEqual(await page.evaluate(`window.search.findNext('abcd', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), false);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 3, resultIndex: 0 },
|
||||
{ resultCount: 2, resultIndex: 0 },
|
||||
{ resultCount: 2, resultIndex: 0 },
|
||||
{ resultCount: 2, resultIndex: 1 },
|
||||
{ resultCount: 2, resultIndex: 1 },
|
||||
{ resultCount: 0, resultIndex: -1 }
|
||||
]);
|
||||
});
|
||||
it('should fire with more than 1k matches', async () => {
|
||||
await page.evaluate(`
|
||||
window.calls = [];
|
||||
window.search.onDidChangeResults(e => window.calls.push(e));
|
||||
`);
|
||||
const data = ('a bc'.repeat(10) + '\\n\\r').repeat(150);
|
||||
await writeSync(page, data);
|
||||
assert.strictEqual(await page.evaluate(`window.search.findNext('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 1000, resultIndex: 0 }
|
||||
]);
|
||||
assert.strictEqual(await page.evaluate(`window.search.findNext('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 1000, resultIndex: 0 },
|
||||
{ resultCount: 1000, resultIndex: 1 }
|
||||
]);
|
||||
assert.strictEqual(await page.evaluate(`window.search.findNext('bc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 1000, resultIndex: 0 },
|
||||
{ resultCount: 1000, resultIndex: 1 },
|
||||
{ resultCount: 1000, resultIndex: 1 }
|
||||
]);
|
||||
});
|
||||
it('should fire when writing to terminal', async () => {
|
||||
await page.evaluate(`
|
||||
window.calls = [];
|
||||
window.search.onDidChangeResults(e => window.calls.push(e));
|
||||
`);
|
||||
await writeSync(page, 'abc bc c\\n\\r'.repeat(2));
|
||||
assert.strictEqual(await page.evaluate(`window.search.findNext('abc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 2, resultIndex: 0 }
|
||||
]);
|
||||
await writeSync(page, 'abc bc c\\n\\r');
|
||||
await timeout(300);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 2, resultIndex: 0 },
|
||||
{ resultCount: 3, resultIndex: 0 }
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('findPrevious', () => {
|
||||
it('should not fire unless the decorations option is set', async () => {
|
||||
@@ -233,6 +282,7 @@ describe('Search Tests', function (): void {
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 1, resultIndex: 0 }
|
||||
]);
|
||||
await page.evaluate(`window.term.clearSelection()`);
|
||||
assert.strictEqual(await page.evaluate(`window.search.findPrevious('b', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 1, resultIndex: 0 },
|
||||
@@ -262,7 +312,7 @@ describe('Search Tests', function (): void {
|
||||
window.calls = [];
|
||||
window.search.onDidChangeResults(e => window.calls.push(e));
|
||||
`);
|
||||
await writeSync(page, 'abc aabc');
|
||||
await writeSync(page, 'd abc aabc d');
|
||||
assert.deepStrictEqual(await page.evaluate(`window.search.findPrevious('a', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 3, resultIndex: 2 }
|
||||
@@ -285,15 +335,64 @@ describe('Search Tests', function (): void {
|
||||
{ resultCount: 2, resultIndex: 1 },
|
||||
{ resultCount: 2, resultIndex: 0 }
|
||||
]);
|
||||
assert.deepStrictEqual(await page.evaluate(`window.search.findPrevious('d', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 3, resultIndex: 2 },
|
||||
{ resultCount: 2, resultIndex: 1 },
|
||||
{ resultCount: 2, resultIndex: 1 },
|
||||
{ resultCount: 2, resultIndex: 0 },
|
||||
{ resultCount: 2, resultIndex: 1 }
|
||||
]);
|
||||
assert.deepStrictEqual(await page.evaluate(`window.search.findPrevious('abcd', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), false);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 3, resultIndex: 2 },
|
||||
{ resultCount: 2, resultIndex: 1 },
|
||||
{ resultCount: 2, resultIndex: 1 },
|
||||
{ resultCount: 2, resultIndex: 0 },
|
||||
{ resultCount: 2, resultIndex: 1 },
|
||||
{ resultCount: 0, resultIndex: -1 }
|
||||
]);
|
||||
});
|
||||
it('should fire with more than 1k matches', async () => {
|
||||
await page.evaluate(`
|
||||
window.calls = [];
|
||||
window.search.onDidChangeResults(e => window.calls.push(e));
|
||||
`);
|
||||
const data = ('a bc'.repeat(10) + '\\n\\r').repeat(150);
|
||||
await writeSync(page, data);
|
||||
assert.strictEqual(await page.evaluate(`window.search.findPrevious('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 1000, resultIndex: -1 }
|
||||
]);
|
||||
assert.strictEqual(await page.evaluate(`window.search.findPrevious('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 1000, resultIndex: -1 },
|
||||
{ resultCount: 1000, resultIndex: -1 }
|
||||
]);
|
||||
assert.strictEqual(await page.evaluate(`window.search.findPrevious('bc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 1000, resultIndex: -1 },
|
||||
{ resultCount: 1000, resultIndex: -1 },
|
||||
{ resultCount: 1000, resultIndex: -1 }
|
||||
]);
|
||||
});
|
||||
it('should fire when writing to terminal', async () => {
|
||||
await page.evaluate(`
|
||||
window.calls = [];
|
||||
window.search.onDidChangeResults(e => window.calls.push(e));
|
||||
`);
|
||||
await writeSync(page, 'abc bc c\\n\\r'.repeat(2));
|
||||
assert.strictEqual(await page.evaluate(`window.search.findPrevious('abc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 2, resultIndex: 1 }
|
||||
]);
|
||||
await writeSync(page, 'abc bc c\\n\\r');
|
||||
await timeout(300);
|
||||
assert.deepStrictEqual(await page.evaluate('window.calls'), [
|
||||
{ resultCount: 2, resultIndex: 1 },
|
||||
{ resultCount: 3, resultIndex: 1 }
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+20
-4
@@ -75,10 +75,28 @@ declare module 'xterm-addon-search' {
|
||||
activeMatchColorOverviewRuler: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for the search addon.
|
||||
*/
|
||||
export interface ISearchAddonOptions {
|
||||
/**
|
||||
* Max number of matches highlighted when decorations are enabled.
|
||||
* Defaults to 1000 highlighted matches
|
||||
*/
|
||||
highlightLimit: number
|
||||
}
|
||||
|
||||
/**
|
||||
* An xterm.js addon that provides search functionality.
|
||||
*/
|
||||
export class SearchAddon implements ITerminalAddon {
|
||||
|
||||
/**
|
||||
* Creates a new search addon.
|
||||
* @param options Options for the search addon.
|
||||
*/
|
||||
constructor(options?: Partial<ISearchAddonOptions>);
|
||||
|
||||
/**
|
||||
* Activates the addon
|
||||
* @param terminal The terminal the addon is being loaded in.
|
||||
@@ -121,10 +139,8 @@ declare module 'xterm-addon-search' {
|
||||
/**
|
||||
* When decorations are enabled, fires when
|
||||
* the search results change.
|
||||
* @returns -1 for resultIndex for a resultCount of 0
|
||||
* and @returns undefined when the threshold of 1k results
|
||||
* is exceeded and decorations are disposed of.
|
||||
* @returns -1 for resultIndex when the threshold of matches is exceeded.
|
||||
*/
|
||||
readonly onDidChangeResults: IEvent<{ resultIndex: number, resultCount: number } | undefined>;
|
||||
readonly onDidChangeResults: IEvent<{ resultIndex: number, resultCount: number }>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-serialize",
|
||||
"version": "0.9.0",
|
||||
"version": "0.10.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -77,6 +77,7 @@ function equalFlags(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): bo
|
||||
return cell1.isInverse() === cell2.isInverse()
|
||||
&& cell1.isBold() === cell2.isBold()
|
||||
&& cell1.isUnderline() === cell2.isUnderline()
|
||||
&& cell1.isOverline() === cell2.isOverline()
|
||||
&& cell1.isBlink() === cell2.isBlink()
|
||||
&& cell1.isInvisible() === cell2.isInvisible()
|
||||
&& cell1.isItalic() === cell2.isItalic()
|
||||
@@ -264,6 +265,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
if (cell.isInverse() !== oldCell.isInverse()) { sgrSeq.push(cell.isInverse() ? 7 : 27); }
|
||||
if (cell.isBold() !== oldCell.isBold()) { sgrSeq.push(cell.isBold() ? 1 : 22); }
|
||||
if (cell.isUnderline() !== oldCell.isUnderline()) { sgrSeq.push(cell.isUnderline() ? 4 : 24); }
|
||||
if (cell.isOverline() !== oldCell.isOverline()) { sgrSeq.push(cell.isOverline() ? 53 : 55); }
|
||||
if (cell.isBlink() !== oldCell.isBlink()) { sgrSeq.push(cell.isBlink() ? 5 : 25); }
|
||||
if (cell.isInvisible() !== oldCell.isInvisible()) { sgrSeq.push(cell.isInvisible() ? 8 : 28); }
|
||||
if (cell.isItalic() !== oldCell.isItalic()) { sgrSeq.push(cell.isItalic() ? 3 : 23); }
|
||||
@@ -625,7 +627,9 @@ export class HTMLSerializeHandler extends BaseSerializeHandler {
|
||||
|
||||
if (cell.isInverse()) { content.push('color: #000000; background-color: #BFBFBF;'); }
|
||||
if (cell.isBold()) { content.push('font-weight: bold;'); }
|
||||
if (cell.isUnderline()) { content.push('text-decoration: underline;'); }
|
||||
if (cell.isUnderline() && cell.isOverline()) { content.push('text-decoration: overline underline;'); }
|
||||
else if (cell.isUnderline()) { content.push('text-decoration: underline;'); }
|
||||
else if (cell.isOverline()) { content.push('text-decoration: overline;'); }
|
||||
if (cell.isBlink()) { content.push('text-decoration: blink;'); }
|
||||
if (cell.isInvisible()) { content.push('visibility: hidden;'); }
|
||||
if (cell.isItalic()) { content.push('font-style: italic;'); }
|
||||
|
||||
@@ -220,6 +220,18 @@ describe('SerializeAddon', () => {
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
});
|
||||
|
||||
it('serialize all rows of content with overline', async () => {
|
||||
const cols = 10;
|
||||
const line = '+'.repeat(cols);
|
||||
const lines: string[] = [
|
||||
sgr(OVERLINED) + line, // Overlined
|
||||
sgr(UNDERLINED) + line, // Overlined, Underlined
|
||||
sgr(NORMAL) + line // Normal
|
||||
];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
});
|
||||
|
||||
it('serialize all rows of content with color16 and style separately', async function(): Promise<any> {
|
||||
const cols = 10;
|
||||
const line = '+'.repeat(cols);
|
||||
@@ -601,6 +613,7 @@ const BLINK = '5';
|
||||
const INVERSE = '7';
|
||||
const INVISIBLE = '8';
|
||||
const STRIKETHROUGH = '9';
|
||||
const OVERLINED = '53';
|
||||
|
||||
const NO_BOLD = '22';
|
||||
const NO_DIM = '22';
|
||||
@@ -610,3 +623,4 @@ const NO_BLINK = '25';
|
||||
const NO_INVERSE = '27';
|
||||
const NO_INVISIBLE = '28';
|
||||
const NO_STRIKETHROUGH = '29';
|
||||
const NO_OVERLINED = '55';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-webgl",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -270,7 +270,7 @@ export class RectangleRenderer extends Disposable {
|
||||
$r = (($rgba >> 24) & 0xFF) / 255;
|
||||
$g = (($rgba >> 16) & 0xFF) / 255;
|
||||
$b = (($rgba >> 8 ) & 0xFF) / 255;
|
||||
$a = (!$isDefault && bg & BgFlags.DIM) ? DIM_OPACITY : 1;
|
||||
$a = 1;
|
||||
|
||||
this._addRectangle(vertices.attributes, offset, $x1, $y1, (endX - startX) * this._dimensions.device.cell.width, this._dimensions.device.cell.height, $r, $g, $b, $a);
|
||||
}
|
||||
|
||||
@@ -336,13 +336,16 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
// Tell renderer the frame is beginning
|
||||
// upon a model clear also refresh the full viewport model
|
||||
// (also triggered by an atlas page merge, part of #4480)
|
||||
if (this._glyphRenderer.beginFrame()) {
|
||||
this._clearModel(true);
|
||||
this._updateModel(0, this._terminal.rows - 1);
|
||||
} else {
|
||||
// just update changed lines to draw
|
||||
this._updateModel(start, end);
|
||||
}
|
||||
|
||||
// Update model to reflect what's drawn
|
||||
this._updateModel(start, end);
|
||||
|
||||
// Render
|
||||
this._rectangleRenderer?.render();
|
||||
this._glyphRenderer?.render(this._model);
|
||||
@@ -366,7 +369,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
let i: number;
|
||||
let x: number;
|
||||
let j: number;
|
||||
|
||||
start = clamp(start, terminal.rows - 1, 0);
|
||||
end = clamp(end, terminal.rows - 1, 0);
|
||||
for (y = start; y <= end; y++) {
|
||||
row = y + terminal.buffer.ydisp;
|
||||
line = terminal.buffer.lines.get(row)!;
|
||||
@@ -570,3 +574,7 @@ export class JoinedCellData extends AttributeData implements ICellData {
|
||||
return [this.fg, this.getChars(), this.getWidth(), this.getCode()];
|
||||
}
|
||||
}
|
||||
|
||||
function clamp(value: number, max: number, min: number = 0): number {
|
||||
return Math.max(Math.min(value, max), min);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { IEventEmitter } from 'common/EventEmitter';
|
||||
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
||||
import { ICoreService, IOptionsService } from 'common/services/Services';
|
||||
import { toDisposable } from 'common/Lifecycle';
|
||||
import { isFirefox } from 'common/Platform';
|
||||
|
||||
interface ICursorState {
|
||||
x: number;
|
||||
@@ -149,11 +150,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this._themeService.colors.cursor.css;
|
||||
const cursorStyle = terminal.options.cursorStyle;
|
||||
if (cursorStyle && cursorStyle !== 'block') {
|
||||
this._cursorRenderers[cursorStyle](terminal, cursorX, viewportRelativeCursorY, this._cell);
|
||||
} else {
|
||||
this._renderBlurCursor(terminal, cursorX, viewportRelativeCursorY, this._cell);
|
||||
}
|
||||
this._renderBlurCursor(terminal, cursorX, viewportRelativeCursorY, this._cell);
|
||||
this._ctx.restore();
|
||||
this._state.x = cursorX;
|
||||
this._state.y = viewportRelativeCursorY;
|
||||
@@ -194,8 +191,9 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
|
||||
private _clearCursor(): void {
|
||||
if (this._state) {
|
||||
// Avoid potential rounding errors when device pixel ratio is less than 1
|
||||
if (this._coreBrowserService.dpr < 1) {
|
||||
// Avoid potential rounding errors when browser is Firefox (#4487) or device pixel ratio is
|
||||
// less than 1
|
||||
if (isFirefox || this._coreBrowserService.dpr < 1) {
|
||||
this._clearAll();
|
||||
} else {
|
||||
this._clearCells(this._state.x, this._state.y, this._state.width, 1);
|
||||
|
||||
@@ -364,6 +364,55 @@ describe('WebGL Renderer Integration Tests', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
itWebgl('foreground 16-255 dim', async () => {
|
||||
let data = '';
|
||||
for (let y = 0; y < 240 / 16; y++) {
|
||||
for (let x = 0; x < 16; x++) {
|
||||
data += `\\x1b[2;38;5;${16 + y * 16 + x}m█\x1b[0m`;
|
||||
}
|
||||
data += '\\r\\n';
|
||||
}
|
||||
await writeSync(page, data);
|
||||
for (let y = 0; y < 240 / 16; y++) {
|
||||
for (let x = 0; x < 16; x++) {
|
||||
const cssColor = COLORS_16_TO_255[y * 16 + x];
|
||||
const r = parseInt(cssColor.slice(1, 3), 16);
|
||||
const g = parseInt(cssColor.slice(3, 5), 16);
|
||||
const b = parseInt(cssColor.slice(5, 7), 16);
|
||||
// It's difficult to assert the exact color due to rounding, just ensure the color differs
|
||||
// to the regular color
|
||||
await pollFor(page, async () => {
|
||||
const c = await getCellColor(x + 1, y + 1);
|
||||
return (
|
||||
(c[0] === 0 || c[0] !== r) &&
|
||||
(c[1] === 0 || c[1] !== g) &&
|
||||
(c[2] === 0 || c[2] !== b)
|
||||
);
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
itWebgl('background 16-255 dim', async () => {
|
||||
let data = '';
|
||||
for (let y = 0; y < 240 / 16; y++) {
|
||||
for (let x = 0; x < 16; x++) {
|
||||
data += `\\x1b[2;48;5;${16 + y * 16 + x}m \\x1b[0m`;
|
||||
}
|
||||
data += '\\r\\n';
|
||||
}
|
||||
await writeSync(page, data);
|
||||
for (let y = 0; y < 240 / 16; y++) {
|
||||
for (let x = 0; x < 16; x++) {
|
||||
const cssColor = COLORS_16_TO_255[y * 16 + x];
|
||||
const r = parseInt(cssColor.slice(1, 3), 16);
|
||||
const g = parseInt(cssColor.slice(3, 5), 16);
|
||||
const b = parseInt(cssColor.slice(5, 7), 16);
|
||||
await pollFor(page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
itWebgl('foreground true color red', async () => {
|
||||
let data = '';
|
||||
for (let y = 0; y < 16; y++) {
|
||||
|
||||
+10
-10
@@ -1,10 +1,10 @@
|
||||
pr:
|
||||
branches:
|
||||
include: ["main", "v5"]
|
||||
include: ["main"]
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include: ["main", "v5"]
|
||||
include: ["main"]
|
||||
|
||||
jobs:
|
||||
- job: Linux
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '14.x'
|
||||
versionSpec: '18.x'
|
||||
displayName: 'Install Node.js'
|
||||
- task: YarnInstaller@3
|
||||
inputs:
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '14.x'
|
||||
versionSpec: '18.x'
|
||||
displayName: 'Install Node.js'
|
||||
- task: CacheBeta@1
|
||||
inputs:
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '14.x'
|
||||
versionSpec: '18.x'
|
||||
displayName: 'Install Node.js'
|
||||
- task: CacheBeta@1
|
||||
inputs:
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
displayName: Install required packages
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '14.x'
|
||||
versionSpec: '18.x'
|
||||
displayName: 'Install Node.js'
|
||||
- task: YarnInstaller@3
|
||||
inputs:
|
||||
@@ -111,11 +111,11 @@ jobs:
|
||||
# Integration tests are too flaky on macOS https://github.com/xtermjs/xterm.js/issues/3590
|
||||
# - job: macOS_IntegrationTests
|
||||
# pool:
|
||||
# vmImage: 'macOS-10.15'
|
||||
# vmImage: 'macOS-11'
|
||||
# steps:
|
||||
# - task: NodeTool@0
|
||||
# inputs:
|
||||
# versionSpec: '14.x'
|
||||
# versionSpec: '18.x'
|
||||
# displayName: 'Install Node.js'
|
||||
# - script: yarn --frozen-lockfile
|
||||
# displayName: 'Install dependencies and build'
|
||||
@@ -132,7 +132,7 @@ jobs:
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '14.x'
|
||||
versionSpec: '18.x'
|
||||
displayName: 'Install Node.js'
|
||||
- script: yarn --frozen-lockfile
|
||||
displayName: 'Install dependencies and build'
|
||||
@@ -155,7 +155,7 @@ jobs:
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '14.x'
|
||||
versionSpec: '18.x'
|
||||
displayName: 'Install Node.js'
|
||||
- task: YarnInstaller@3
|
||||
inputs:
|
||||
|
||||
+19
-21
@@ -149,25 +149,7 @@
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.xterm .xterm-accessibility-buffer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: .5em;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
opacity: 0;
|
||||
overflow: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.xterm .xterm-accessibility-buffer:focus {
|
||||
opacity: 1;
|
||||
z-index: 20;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.xterm .live-region {
|
||||
@@ -179,7 +161,9 @@
|
||||
}
|
||||
|
||||
.xterm-dim {
|
||||
opacity: 0.5;
|
||||
/* Dim should not apply to background, so the opacity of the foreground color is applied
|
||||
* explicitly in the generated class and reset to 1 here */
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.xterm-underline-1 { text-decoration: underline; }
|
||||
@@ -188,6 +172,16 @@
|
||||
.xterm-underline-4 { text-decoration: dotted underline; }
|
||||
.xterm-underline-5 { text-decoration: dashed underline; }
|
||||
|
||||
.xterm-overline {
|
||||
text-decoration: overline;
|
||||
}
|
||||
|
||||
.xterm-overline.xterm-underline-1 { text-decoration: overline underline; }
|
||||
.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; }
|
||||
.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; }
|
||||
.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; }
|
||||
.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; }
|
||||
|
||||
.xterm-strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
@@ -197,8 +191,12 @@
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer {
|
||||
z-index: 7;
|
||||
}
|
||||
|
||||
.xterm-decoration-overview-ruler {
|
||||
z-index: 7;
|
||||
z-index: 8;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
+27
-9
@@ -132,12 +132,11 @@ function setPadding(): void {
|
||||
addons.fit.instance.fit();
|
||||
}
|
||||
|
||||
function getSearchOptions(e: KeyboardEvent): ISearchOptions {
|
||||
function getSearchOptions(): ISearchOptions {
|
||||
return {
|
||||
regex: (document.getElementById('regex') as HTMLInputElement).checked,
|
||||
wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked,
|
||||
caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked,
|
||||
incremental: e.key !== `Enter`,
|
||||
decorations: (document.getElementById('highlight-all-matches') as HTMLInputElement).checked ? {
|
||||
matchBackground: '#232422',
|
||||
matchBorder: '#555753',
|
||||
@@ -241,7 +240,11 @@ function createTerminal(): void {
|
||||
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0;
|
||||
term = new Terminal({
|
||||
allowProposedApi: true,
|
||||
windowsMode: isWindows,
|
||||
windowsPty: isWindows ? {
|
||||
// In a real scenario, these values should be verified on the backend
|
||||
backend: 'conpty',
|
||||
buildNumber: 22621
|
||||
} : undefined,
|
||||
fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"',
|
||||
theme: xtermjsTheme
|
||||
} as ITerminalOptions);
|
||||
@@ -303,11 +306,23 @@ function createTerminal(): void {
|
||||
|
||||
addDomListener(paddingElement, 'change', setPadding);
|
||||
|
||||
addDomListener(actionElements.findNext, 'keyup', (e) => {
|
||||
addons.search.instance.findNext(actionElements.findNext.value, getSearchOptions(e));
|
||||
addDomListener(actionElements.findNext, 'keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
addons.search.instance.findNext(actionElements.findNext.value, getSearchOptions());
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
addDomListener(actionElements.findPrevious, 'keyup', (e) => {
|
||||
addons.search.instance.findPrevious(actionElements.findPrevious.value, getSearchOptions(e));
|
||||
addDomListener(actionElements.findNext, 'input', (e) => {
|
||||
addons.search.instance.findNext(actionElements.findNext.value, getSearchOptions());
|
||||
});
|
||||
addDomListener(actionElements.findPrevious, 'keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
addons.search.instance.findPrevious(actionElements.findPrevious.value, getSearchOptions());
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
addDomListener(actionElements.findPrevious, 'input', (e) => {
|
||||
addons.search.instance.findPrevious(actionElements.findPrevious.value, getSearchOptions());
|
||||
});
|
||||
addDomListener(actionElements.findNext, 'blur', (e) => {
|
||||
addons.search.instance.clearActiveDecoration();
|
||||
@@ -965,7 +980,9 @@ function sgrTest(): void {
|
||||
{ ps: 45, name: 'Background Magenta' },
|
||||
{ ps: 46, name: 'Background Cyan' },
|
||||
{ ps: 47, name: 'Background White' },
|
||||
{ ps: 49, name: 'Background default' }
|
||||
{ ps: 49, name: 'Background default' },
|
||||
{ ps: 53, name: 'Overlined' },
|
||||
{ ps: 55, name: 'Not overlined' }
|
||||
];
|
||||
const maxNameLength = entries.reduce<number>((p, c) => Math.max(c.name.length, p), 0);
|
||||
for (const e of entries) {
|
||||
@@ -977,7 +994,8 @@ function sgrTest(): void {
|
||||
}
|
||||
const comboEntries: { ps: number[] }[] = [
|
||||
{ ps: [1, 2, 3, 4, 5, 6, 7, 9] },
|
||||
{ ps: [2, 41] }
|
||||
{ ps: [2, 41] },
|
||||
{ ps: [4, 53] }
|
||||
];
|
||||
term.write('\n\n\r');
|
||||
term.writeln(`Combinations`);
|
||||
|
||||
+12
-15
@@ -111,35 +111,32 @@ function startServer() {
|
||||
}
|
||||
// binary message buffering
|
||||
function bufferUtf8(socket, timeout, maxSize) {
|
||||
const dataBuffer = new Uint8Array(maxSize);
|
||||
let sender = null;
|
||||
const chunks = [];
|
||||
let length = 0;
|
||||
let sender = null;
|
||||
return (data) => {
|
||||
function flush() {
|
||||
socket.send(Buffer.from(dataBuffer.buffer, 0, length));
|
||||
chunks.push(data);
|
||||
length += data.length;
|
||||
if (length > maxSize || userInput) {
|
||||
userInput = false;
|
||||
socket.send(Buffer.concat(chunks));
|
||||
chunks.length = 0;
|
||||
length = 0;
|
||||
if (sender) {
|
||||
clearTimeout(sender);
|
||||
sender = null;
|
||||
}
|
||||
}
|
||||
if (length + data.length > maxSize) {
|
||||
flush();
|
||||
}
|
||||
dataBuffer.set(data, length);
|
||||
length += data.length;
|
||||
if (length > maxSize || userInput) {
|
||||
userInput = false;
|
||||
flush();
|
||||
} else if (!sender) {
|
||||
sender = setTimeout(() => {
|
||||
socket.send(Buffer.concat(chunks));
|
||||
chunks.length = 0;
|
||||
length = 0;
|
||||
sender = null;
|
||||
flush();
|
||||
}, timeout);
|
||||
}
|
||||
};
|
||||
}
|
||||
const send = (USE_BINARY ? bufferUtf8 : buffer)(ws, 5, 262144);
|
||||
const send = (USE_BINARY ? bufferUtf8 : buffer)(ws, 3, 262144);
|
||||
|
||||
// WARNING: This is a naive implementation that will not throttle the flow of data. This means
|
||||
// it could flood the communication channel and make the terminal unresponsive. Learn more about
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "xterm",
|
||||
"description": "Full xterm terminal, in your browser",
|
||||
"version": "5.1.0",
|
||||
"version": "5.2.1",
|
||||
"main": "lib/xterm.js",
|
||||
"style": "css/xterm.css",
|
||||
"types": "typings/xterm.d.ts",
|
||||
@@ -58,7 +58,7 @@
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/jsdom": "^16.2.13",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^14.14.44",
|
||||
"@types/node": "^18.16.0",
|
||||
"@types/utf8": "^3.0.0",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@types/ws": "^8.2.0",
|
||||
|
||||
@@ -4,25 +4,36 @@
|
||||
*/
|
||||
|
||||
import * as Strings from 'browser/LocalizableStrings';
|
||||
import { ITerminal, IRenderDebouncer, ReadonlyColorSet } from 'browser/Types';
|
||||
import { ITerminal, IRenderDebouncer } from 'browser/Types';
|
||||
import { isMac } from 'common/Platform';
|
||||
import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
import { IRenderService, IThemeService } from 'browser/services/Services';
|
||||
import { IOptionsService } from 'common/services/Services';
|
||||
import { ITerminalOptions } from 'xterm';
|
||||
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
|
||||
import { IRenderService } from 'browser/services/Services';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
|
||||
const MAX_ROWS_TO_READ = 20;
|
||||
|
||||
const enum BoundaryPosition {
|
||||
TOP,
|
||||
BOTTOM
|
||||
}
|
||||
|
||||
export class AccessibilityManager extends Disposable {
|
||||
private _accessibilityContainer: HTMLElement;
|
||||
private _accessiblityBuffer: HTMLElement;
|
||||
|
||||
private _rowContainer: HTMLElement;
|
||||
private _rowElements: HTMLElement[];
|
||||
|
||||
private _liveRegion: HTMLElement;
|
||||
private _liveRegionLineCount: number = 0;
|
||||
private _liveRegionDebouncer: IRenderDebouncer;
|
||||
|
||||
private _screenDprMonitor: ScreenDprMonitor;
|
||||
|
||||
private _topBoundaryFocusListener: (e: FocusEvent) => void;
|
||||
private _bottomBoundaryFocusListener: (e: FocusEvent) => void;
|
||||
|
||||
/**
|
||||
* This queue has a character pushed to it for keys that are pressed, if the
|
||||
* next character added to the terminal is equal to the key char then it is
|
||||
@@ -36,19 +47,31 @@ export class AccessibilityManager extends Disposable {
|
||||
|
||||
private _charsToAnnounce: string = '';
|
||||
|
||||
private _isAccessibilityBufferActive: boolean = false;
|
||||
public get isAccessibilityBufferActive(): boolean { return this._isAccessibilityBufferActive; }
|
||||
|
||||
constructor(
|
||||
private readonly _terminal: ITerminal,
|
||||
@IOptionsService optionsService: IOptionsService,
|
||||
@IRenderService private readonly _renderService: IRenderService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IRenderService private readonly _renderService: IRenderService
|
||||
) {
|
||||
super();
|
||||
this._accessibilityContainer = document.createElement('div');
|
||||
this._accessibilityContainer.classList.add('xterm-accessibility');
|
||||
|
||||
this._rowContainer = document.createElement('div');
|
||||
this._rowContainer.setAttribute('role', 'list');
|
||||
this._rowContainer.classList.add('xterm-accessibility-tree');
|
||||
this._rowElements = [];
|
||||
for (let i = 0; i < this._terminal.rows; i++) {
|
||||
this._rowElements[i] = this._createAccessibilityTreeNode();
|
||||
this._rowContainer.appendChild(this._rowElements[i]);
|
||||
}
|
||||
|
||||
this._topBoundaryFocusListener = e => this._handleBoundaryFocus(e, BoundaryPosition.TOP);
|
||||
this._bottomBoundaryFocusListener = e => this._handleBoundaryFocus(e, BoundaryPosition.BOTTOM);
|
||||
this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);
|
||||
this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
|
||||
|
||||
this._refreshRowsDimensions();
|
||||
this._accessibilityContainer.appendChild(this._rowContainer);
|
||||
|
||||
this._liveRegion = document.createElement('div');
|
||||
this._liveRegion.classList.add('live-region');
|
||||
this._liveRegion.setAttribute('aria-live', 'assertive');
|
||||
@@ -60,24 +83,8 @@ export class AccessibilityManager extends Disposable {
|
||||
}
|
||||
this._terminal.element.insertAdjacentElement('afterbegin', this._accessibilityContainer);
|
||||
|
||||
this._accessiblityBuffer = document.createElement('div');
|
||||
this._accessiblityBuffer.setAttribute('role', 'document');
|
||||
this._accessiblityBuffer.ariaRoleDescription = Strings.accessibilityBuffer;
|
||||
this._accessiblityBuffer.tabIndex = 0;
|
||||
this._accessibilityContainer.appendChild(this._accessiblityBuffer);
|
||||
this._accessiblityBuffer.classList.add('xterm-accessibility-buffer');
|
||||
this.register(addDisposableDomListener(this._accessiblityBuffer, 'keydown', (ev: KeyboardEvent) => {
|
||||
if (ev.key === 'Tab') {
|
||||
this._isAccessibilityBufferActive = false;
|
||||
}}
|
||||
));
|
||||
this.register(addDisposableDomListener(this._accessiblityBuffer, 'focus',() => this._refreshAccessibilityBuffer()));
|
||||
this.register(addDisposableDomListener(this._accessiblityBuffer, 'focusout',() => {
|
||||
this._isAccessibilityBufferActive = false;
|
||||
}));
|
||||
|
||||
|
||||
this.register(this._liveRegionDebouncer);
|
||||
this.register(this._terminal.onResize(e => this._handleResize(e.rows)));
|
||||
this.register(this._terminal.onRender(e => this._refreshRows(e.start, e.end)));
|
||||
this.register(this._terminal.onScroll(() => this._refreshRows()));
|
||||
// Line feed is an issue as the prompt won't be read out after a command is run
|
||||
@@ -86,15 +93,19 @@ export class AccessibilityManager extends Disposable {
|
||||
this.register(this._terminal.onA11yTab(spaceCount => this._handleTab(spaceCount)));
|
||||
this.register(this._terminal.onKey(e => this._handleKey(e.key)));
|
||||
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
|
||||
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
|
||||
|
||||
this._handleColorChange(themeService.colors);
|
||||
this.register(themeService.onChangeColors(e => this._handleColorChange(e)));
|
||||
this._handleFontOptionChange(optionsService.options);
|
||||
this.register(optionsService.onMultipleOptionChange(['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight'], () => this._handleFontOptionChange(optionsService.options)));
|
||||
this._screenDprMonitor = new ScreenDprMonitor(window);
|
||||
this.register(this._screenDprMonitor);
|
||||
this._screenDprMonitor.setListener(() => this._refreshRowsDimensions());
|
||||
// This shouldn't be needed on modern browsers but is present in case the
|
||||
// media query that drives the ScreenDprMonitor isn't supported
|
||||
this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions()));
|
||||
|
||||
this._refreshRows();
|
||||
this.register(toDisposable(() => {
|
||||
this._accessiblityBuffer.remove();
|
||||
this._accessibilityContainer.remove();
|
||||
this._rowElements.length = 0;
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -156,10 +167,6 @@ export class AccessibilityManager extends Disposable {
|
||||
this._liveRegionDebouncer.refresh(start, end, this._terminal.rows);
|
||||
}
|
||||
|
||||
private _refreshRowDimensions(element: HTMLElement): void {
|
||||
element.style.height = `${this._renderService.dimensions.css.cell.height}px`;
|
||||
}
|
||||
|
||||
private _announceCharacters(): void {
|
||||
if (this._charsToAnnounce.length === 0) {
|
||||
return;
|
||||
@@ -168,31 +175,106 @@ export class AccessibilityManager extends Disposable {
|
||||
this._charsToAnnounce = '';
|
||||
}
|
||||
|
||||
private _handleBoundaryFocus(e: FocusEvent, position: BoundaryPosition): void {
|
||||
const boundaryElement = e.target as HTMLElement;
|
||||
const beforeBoundaryElement = this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2];
|
||||
|
||||
private _refreshAccessibilityBuffer(): void {
|
||||
if (!this._terminal.viewport) {
|
||||
// Don't scroll if the buffer top has reached the end in that direction
|
||||
const posInSet = boundaryElement.getAttribute('aria-posinset');
|
||||
const lastRowPos = position === BoundaryPosition.TOP ? '1' : `${this._terminal.buffer.lines.length}`;
|
||||
if (posInSet === lastRowPos) {
|
||||
return;
|
||||
}
|
||||
this._isAccessibilityBufferActive = true;
|
||||
const { bufferElements } = this._terminal.viewport.getBufferElements(0);
|
||||
for (const element of bufferElements) {
|
||||
if (element.textContent) {
|
||||
element.textContent = element.textContent.replace(new RegExp(' ', 'g'), '\xA0');
|
||||
}
|
||||
|
||||
// Don't scroll when the last focused item was not the second row (focus is going the other
|
||||
// direction)
|
||||
if (e.relatedTarget !== beforeBoundaryElement) {
|
||||
return;
|
||||
}
|
||||
this._accessiblityBuffer.replaceChildren(...bufferElements);
|
||||
this._accessiblityBuffer.scrollTop = this._accessiblityBuffer.scrollHeight;
|
||||
|
||||
// Remove old boundary element from array
|
||||
let topBoundaryElement: HTMLElement;
|
||||
let bottomBoundaryElement: HTMLElement;
|
||||
if (position === BoundaryPosition.TOP) {
|
||||
topBoundaryElement = boundaryElement;
|
||||
bottomBoundaryElement = this._rowElements.pop()!;
|
||||
this._rowContainer.removeChild(bottomBoundaryElement);
|
||||
} else {
|
||||
topBoundaryElement = this._rowElements.shift()!;
|
||||
bottomBoundaryElement = boundaryElement;
|
||||
this._rowContainer.removeChild(topBoundaryElement);
|
||||
}
|
||||
|
||||
// Remove listeners from old boundary elements
|
||||
topBoundaryElement.removeEventListener('focus', this._topBoundaryFocusListener);
|
||||
bottomBoundaryElement.removeEventListener('focus', this._bottomBoundaryFocusListener);
|
||||
|
||||
// Add new element to array/DOM
|
||||
if (position === BoundaryPosition.TOP) {
|
||||
const newElement = this._createAccessibilityTreeNode();
|
||||
this._rowElements.unshift(newElement);
|
||||
this._rowContainer.insertAdjacentElement('afterbegin', newElement);
|
||||
} else {
|
||||
const newElement = this._createAccessibilityTreeNode();
|
||||
this._rowElements.push(newElement);
|
||||
this._rowContainer.appendChild(newElement);
|
||||
}
|
||||
|
||||
// Add listeners to new boundary elements
|
||||
this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);
|
||||
this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
|
||||
|
||||
// Scroll up
|
||||
this._terminal.scrollLines(position === BoundaryPosition.TOP ? -1 : 1);
|
||||
|
||||
// Focus new boundary before element
|
||||
this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2].focus();
|
||||
|
||||
// Prevent the standard behavior
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
private _handleColorChange(colorSet: ReadonlyColorSet): void {
|
||||
this._accessiblityBuffer.style.backgroundColor = colorSet.background.css;
|
||||
this._accessiblityBuffer.style.color = colorSet.foreground.css;
|
||||
private _handleResize(rows: number): void {
|
||||
// Remove bottom boundary listener
|
||||
this._rowElements[this._rowElements.length - 1].removeEventListener('focus', this._bottomBoundaryFocusListener);
|
||||
|
||||
// Grow rows as required
|
||||
for (let i = this._rowContainer.children.length; i < this._terminal.rows; i++) {
|
||||
this._rowElements[i] = this._createAccessibilityTreeNode();
|
||||
this._rowContainer.appendChild(this._rowElements[i]);
|
||||
}
|
||||
// Shrink rows as required
|
||||
while (this._rowElements.length > rows) {
|
||||
this._rowContainer.removeChild(this._rowElements.pop()!);
|
||||
}
|
||||
|
||||
// Add bottom boundary listener
|
||||
this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
|
||||
|
||||
this._refreshRowsDimensions();
|
||||
}
|
||||
|
||||
private _handleFontOptionChange(options: Required<ITerminalOptions>): void {
|
||||
this._accessiblityBuffer.style.fontFamily = options.fontFamily;
|
||||
this._accessiblityBuffer.style.fontSize = `${options.fontSize}px`;
|
||||
this._accessiblityBuffer.style.lineHeight = `${options.lineHeight * this._renderService.dimensions.css.cell.height}px`;
|
||||
this._accessiblityBuffer.style.letterSpacing = `${options.letterSpacing}px`;
|
||||
private _createAccessibilityTreeNode(): HTMLElement {
|
||||
const element = document.createElement('div');
|
||||
element.setAttribute('role', 'listitem');
|
||||
element.tabIndex = -1;
|
||||
this._refreshRowDimensions(element);
|
||||
return element;
|
||||
}
|
||||
private _refreshRowsDimensions(): void {
|
||||
if (!this._renderService.dimensions.css.cell.height) {
|
||||
return;
|
||||
}
|
||||
this._accessibilityContainer.style.width = `${this._renderService.dimensions.css.canvas.width}px`;
|
||||
if (this._rowElements.length !== this._terminal.rows) {
|
||||
this._handleResize(this._terminal.rows);
|
||||
}
|
||||
for (let i = 0; i < this._terminal.rows; i++) {
|
||||
this._refreshRowDimensions(this._rowElements[i]);
|
||||
}
|
||||
}
|
||||
private _refreshRowDimensions(element: HTMLElement): void {
|
||||
element.style.height = `${this._renderService.dimensions.css.cell.height}px`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,3 @@ export let promptLabel = 'Terminal input';
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
export let tooMuchOutput = 'Too much output to announce, navigate to rows manually to read';
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
export let accessibilityBuffer = 'Accessibility buffer';
|
||||
|
||||
@@ -1041,6 +1041,47 @@ describe('Terminal', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Windows Pty', () => {
|
||||
it('should mark lines as wrapped when the line ends in a non-null character after a LF', async () => {
|
||||
const data = [
|
||||
'aaaaaaaaaa\n\r', // cannot wrap as it's the first
|
||||
'aaaaaaaaa\n\r', // wrapped (windows mode only)
|
||||
'aaaaaaaaa' // not wrapped
|
||||
];
|
||||
|
||||
const normalTerminal = new TestTerminal({ rows: 5, cols: 10, windowsPty: {} });
|
||||
await normalTerminal.writeP(data.join(''));
|
||||
assert.equal(normalTerminal.buffer.lines.get(0)!.isWrapped, false);
|
||||
assert.equal(normalTerminal.buffer.lines.get(1)!.isWrapped, false);
|
||||
assert.equal(normalTerminal.buffer.lines.get(2)!.isWrapped, false);
|
||||
|
||||
const windowsModeTerminal = new TestTerminal({ rows: 5, cols: 10, windowsPty: { backend: 'conpty', buildNumber: 19000 } });
|
||||
await windowsModeTerminal.writeP(data.join(''));
|
||||
assert.equal(windowsModeTerminal.buffer.lines.get(0)!.isWrapped, false);
|
||||
assert.equal(windowsModeTerminal.buffer.lines.get(1)!.isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character');
|
||||
assert.equal(windowsModeTerminal.buffer.lines.get(2)!.isWrapped, false);
|
||||
});
|
||||
|
||||
it('should mark lines as wrapped when the line ends in a non-null character after a CUP', async () => {
|
||||
const data = [
|
||||
'aaaaaaaaaa\x1b[2;1H', // cannot wrap as it's the first
|
||||
'aaaaaaaaa\x1b[3;1H', // wrapped (windows mode only)
|
||||
'aaaaaaaaa' // not wrapped
|
||||
];
|
||||
|
||||
const normalTerminal = new TestTerminal({ rows: 5, cols: 10, windowsPty: {} });
|
||||
await normalTerminal.writeP(data.join(''));
|
||||
assert.equal(normalTerminal.buffer.lines.get(0)!.isWrapped, false);
|
||||
assert.equal(normalTerminal.buffer.lines.get(1)!.isWrapped, false);
|
||||
assert.equal(normalTerminal.buffer.lines.get(2)!.isWrapped, false);
|
||||
|
||||
const windowsModeTerminal = new TestTerminal({ rows: 5, cols: 10, windowsPty: { backend: 'conpty', buildNumber: 19000 } });
|
||||
await windowsModeTerminal.writeP(data.join(''));
|
||||
assert.equal(windowsModeTerminal.buffer.lines.get(0)!.isWrapped, false);
|
||||
assert.equal(windowsModeTerminal.buffer.lines.get(1)!.isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character');
|
||||
assert.equal(windowsModeTerminal.buffer.lines.get(2)!.isWrapped, false);
|
||||
});
|
||||
});
|
||||
describe('Windows Mode', () => {
|
||||
it('should mark lines as wrapped when the line ends in a non-null character after a LF', async () => {
|
||||
const data = [
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* http://linux.die.net/man/7/urxvt
|
||||
*/
|
||||
|
||||
import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, IViewport, ILinkifier2, CharacterJoinerHandler, IBufferRange } from 'browser/Types';
|
||||
import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, IViewport, ILinkifier2, CharacterJoinerHandler, IBufferRange, IBufferElementProvider } from 'browser/Types';
|
||||
import { IRenderer } from 'browser/renderer/shared/Types';
|
||||
import { CompositionHelper } from 'browser/input/CompositionHelper';
|
||||
import { Viewport } from 'browser/Viewport';
|
||||
@@ -767,9 +767,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
*/
|
||||
this.register(addDisposableDomListener(el, 'mousedown', (ev: MouseEvent) => {
|
||||
ev.preventDefault();
|
||||
if (this._accessibilityManager?.isAccessibilityBufferActive) {
|
||||
return;
|
||||
}
|
||||
this.focus();
|
||||
|
||||
// Don't send the mouse button to the pty if mouse events are disabled or
|
||||
@@ -920,7 +917,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
return this.buffer.markers;
|
||||
}
|
||||
|
||||
public addMarker(cursorYOffset: number): IMarker | undefined {
|
||||
public addMarker(cursorYOffset: number): IMarker {
|
||||
return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration }
|
||||
import { IEvent, EventEmitter } from 'common/EventEmitter';
|
||||
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
||||
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet } from 'browser/Types';
|
||||
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from 'browser/Types';
|
||||
import { IBuffer, IBufferSet } from 'common/buffer/Types';
|
||||
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions, ColorIndex } from 'common/Types';
|
||||
import { Buffer } from 'common/buffer/Buffer';
|
||||
@@ -143,6 +143,9 @@ export class MockTerminal implements ITerminal {
|
||||
public getBufferElements(startLine: number, endLine?: number | undefined): { bufferElements: HTMLElement[], cursorElement?: HTMLElement | undefined } {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public registerBufferElementProvider(bufferProvider: IBufferElementProvider): IDisposable {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public bracketedPasteMode!: boolean;
|
||||
public renderer!: IRenderer;
|
||||
public linkifier2!: ILinkifier2;
|
||||
|
||||
Vendored
+5
-1
@@ -59,7 +59,7 @@ export interface IPublicTerminal extends IDisposable {
|
||||
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
|
||||
registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
|
||||
deregisterCharacterJoiner(joinerId: number): void;
|
||||
addMarker(cursorYOffset: number): IMarker | undefined;
|
||||
addMarker(cursorYOffset: number): IMarker;
|
||||
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
|
||||
hasSelection(): boolean;
|
||||
getSelection(): string;
|
||||
@@ -220,3 +220,7 @@ export interface IRenderDebouncer extends IDisposable {
|
||||
export interface IRenderDebouncerWithCallback extends IRenderDebouncer {
|
||||
addRefreshCallback(callback: FrameRequestCallback): number;
|
||||
}
|
||||
|
||||
export interface IBufferElementProvider {
|
||||
provideBufferElements(): DocumentFragment | HTMLElement;
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ export class BufferDecorationRenderer extends Disposable {
|
||||
private _createElement(decoration: IInternalDecoration): HTMLElement {
|
||||
const element = document.createElement('div');
|
||||
element.classList.add('xterm-decoration');
|
||||
element.classList.toggle('xterm-decoration-top-layer', decoration?.options?.layer === 'top');
|
||||
element.style.width = `${Math.round((decoration.options.width || 1) * this._renderService.dimensions.css.cell.width)}px`;
|
||||
element.style.height = `${(decoration.options.height || 1) * this._renderService.dimensions.css.cell.height}px`;
|
||||
element.style.top = `${(decoration.marker.line - this._bufferService.buffers.active.ydisp) * this._renderService.dimensions.css.cell.height}px`;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Terminal as ITerminalApi, IMarker, IDisposable, ILocalizableStrings, ITerminalAddon, IBufferNamespace as IBufferNamespaceApi, IParser, ILinkProvider, IUnicodeHandling, IModes, IDecorationOptions, IDecoration } from 'xterm';
|
||||
import { Terminal as ITerminalApi, IMarker, IDisposable, ILocalizableStrings, ITerminalAddon, IBufferNamespace as IBufferNamespaceApi, IParser, ILinkProvider, IUnicodeHandling, IModes, IDecorationOptions, IDecoration, IBufferElementProvider } from 'xterm';
|
||||
import { IBufferRange, ITerminal } from 'browser/Types';
|
||||
import { Terminal as TerminalCore } from 'browser/Terminal';
|
||||
import * as Strings from 'browser/LocalizableStrings';
|
||||
@@ -156,7 +156,7 @@ export class Terminal implements ITerminalApi {
|
||||
this._checkProposedApi();
|
||||
this._core.deregisterCharacterJoiner(joinerId);
|
||||
}
|
||||
public registerMarker(cursorYOffset: number = 0): IMarker | undefined {
|
||||
public registerMarker(cursorYOffset: number = 0): IMarker {
|
||||
this._verifyIntegers(cursorYOffset);
|
||||
return this._core.addMarker(cursorYOffset);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { BOLD_CLASS, CURSOR_BLINK_CLASS, CURSOR_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory, ITALIC_CLASS } from 'browser/renderer/dom/DomRendererRowFactory';
|
||||
import { BOLD_CLASS, CURSOR_BLINK_CLASS, CURSOR_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DIM_CLASS, DomRendererRowFactory, ITALIC_CLASS } from 'browser/renderer/dom/DomRendererRowFactory';
|
||||
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
|
||||
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
|
||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
||||
@@ -53,7 +53,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService private readonly _themeService: IThemeService
|
||||
) {
|
||||
super();
|
||||
this._rowContainer = document.createElement('div');
|
||||
@@ -69,8 +69,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
this._updateDimensions();
|
||||
this.register(this._optionsService.onOptionChange(() => this._handleOptionsChanged()));
|
||||
|
||||
this.register(themeService.onChangeColors(e => this._injectCss(e)));
|
||||
this._injectCss(themeService.colors);
|
||||
this.register(this._themeService.onChangeColors(e => this._injectCss(e)));
|
||||
this._injectCss(this._themeService.colors);
|
||||
|
||||
this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document);
|
||||
|
||||
@@ -149,6 +149,10 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
` font-family: ${this._optionsService.rawOptions.fontFamily};` +
|
||||
` font-size: ${this._optionsService.rawOptions.fontSize}px;` +
|
||||
`}`;
|
||||
styles +=
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .xterm-dim {` +
|
||||
` color: ${color.multiplyOpacity(colors.foreground, 0.5).css};` +
|
||||
`}`;
|
||||
// Text styles
|
||||
styles +=
|
||||
`${this._terminalSelector} span:not(.${BOLD_CLASS}) {` +
|
||||
@@ -180,7 +184,10 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
`}`;
|
||||
// Cursor
|
||||
styles +=
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} ,` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} ,` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} ` +
|
||||
`{` +
|
||||
` outline: 1px solid ${colors.cursor.css};` +
|
||||
` outline-offset: -1px;` +
|
||||
`}` +
|
||||
@@ -221,10 +228,12 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
for (const [i, c] of colors.ansi.entries()) {
|
||||
styles +=
|
||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
|
||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i}.${DIM_CLASS} { color: ${color.multiplyOpacity(c, 0.5).css}; }` +
|
||||
`${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
|
||||
}
|
||||
styles +=
|
||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(colors.background).css}; }` +
|
||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${DIM_CLASS} { color: ${color.multiplyOpacity(color.opaque(colors.background), 0.5).css}; }` +
|
||||
`${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${colors.foreground.css}; }`;
|
||||
|
||||
this._themeStyleElement.textContent = styles;
|
||||
@@ -337,6 +346,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
private _handleOptionsChanged(): void {
|
||||
// Force a refresh
|
||||
this._updateDimensions();
|
||||
// Refresh CSS
|
||||
this._injectCss(this._themeService.colors);
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
@@ -382,8 +393,37 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
private _setCellUnderline(x: number, x2: number, y: number, y2: number, cols: number, enabled: boolean): void {
|
||||
x = this._cellToRowElements[y][x];
|
||||
x2 = this._cellToRowElements[y2][x2];
|
||||
/**
|
||||
* NOTE: The linkifier may send out of viewport y-values if:
|
||||
* - negative y-value: the link started at a higher line
|
||||
* - y-value >= maxY: the link ends at a line below viewport
|
||||
*
|
||||
* For negative y-values we can simply adjust x = 0,
|
||||
* as higher up link start means, that everything from
|
||||
* (0,0) is a link under top-down-left-right char progression
|
||||
*
|
||||
* Additionally there might be a small chance of out-of-sync x|y-values
|
||||
* from a race condition of render updates vs. link event handler execution:
|
||||
* - (sync) resize: chances terminal buffer in sync, schedules render update async
|
||||
* - (async) link handler race condition: new buffer metrics, but still on old render state
|
||||
* - (async) render update: brings term metrics and render state back in sync
|
||||
*/
|
||||
if (y < 0) x = 0;
|
||||
if (y2 < 0) x2 = 0;
|
||||
|
||||
// avoid out-of-sync y-values, simply clamp into valid area
|
||||
const maxY = this._cellToRowElements.length - 1;
|
||||
y = Math.max(Math.min(y, maxY), 0);
|
||||
y2 = Math.max(Math.min(y2, maxY), 0);
|
||||
const elemY = this._cellToRowElements[y];
|
||||
const elemY2 = this._cellToRowElements[y2];
|
||||
if (x >= elemY.length || x2 >= elemY2.length) {
|
||||
// avoid out-of-sync x-values
|
||||
// simply exit early, gets fixed by the next render update
|
||||
return;
|
||||
}
|
||||
x = elemY[x];
|
||||
x2 = elemY2[x2];
|
||||
|
||||
if (x === -1 || x2 === -1) {
|
||||
return;
|
||||
|
||||
@@ -167,6 +167,16 @@ describe('DomRendererRowFactory', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should add class for overline', () => {
|
||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.OVERLINE;
|
||||
lineData.setCell(0, cell);
|
||||
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_ELEM_MAPPING);
|
||||
assert.equal(getFragmentHtml(fragment),
|
||||
'<span class="xterm-overline">a</span>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should add class for strikethrough', () => {
|
||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.STRIKETHROUGH;
|
||||
|
||||
@@ -9,7 +9,6 @@ import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
import { color, rgba } from 'common/Color';
|
||||
import { IColorSet, ReadonlyColorSet } from 'browser/Types';
|
||||
import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
||||
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
|
||||
import { excludeFromContrastRatioDemands } from 'browser/renderer/shared/RendererUtils';
|
||||
@@ -19,6 +18,7 @@ export const BOLD_CLASS = 'xterm-bold';
|
||||
export const DIM_CLASS = 'xterm-dim';
|
||||
export const ITALIC_CLASS = 'xterm-italic';
|
||||
export const UNDERLINE_CLASS = 'xterm-underline';
|
||||
export const OVERLINE_CLASS = 'xterm-overline';
|
||||
export const STRIKETHROUGH_CLASS = 'xterm-strikethrough';
|
||||
export const CURSOR_CLASS = 'xterm-cursor';
|
||||
export const CURSOR_BLINK_CLASS = 'xterm-cursor-blink';
|
||||
@@ -186,6 +186,13 @@ export class DomRendererRowFactory {
|
||||
}
|
||||
}
|
||||
|
||||
if (cell.isOverline()) {
|
||||
charElement.classList.add(OVERLINE_CLASS);
|
||||
if (charElement.textContent === ' ') {
|
||||
charElement.textContent = '\xa0'; // =
|
||||
}
|
||||
}
|
||||
|
||||
if (cell.isStrikethrough()) {
|
||||
charElement.classList.add(STRIKETHROUGH_CLASS);
|
||||
}
|
||||
|
||||
@@ -383,10 +383,10 @@ export const powerlineDefinitions: { [index: string]: IVectorShape } = {
|
||||
// Upper right triangle
|
||||
'\u{E0BE}': { d: 'M-.5,-.5 L1.5,1.5 L1.5,-.5', type: VectorType.FILL }
|
||||
};
|
||||
// Backslash separator redundant
|
||||
powerlineDefinitions['\u{E0BB}'] = powerlineDefinitions['\u{E0B9}'];
|
||||
// Forward slash separator redundant
|
||||
powerlineDefinitions['\u{E0BF}'] = powerlineDefinitions['\u{E0BD}'];
|
||||
powerlineDefinitions['\u{E0BB}'] = powerlineDefinitions['\u{E0BD}'];
|
||||
// Backslash separator redundant
|
||||
powerlineDefinitions['\u{E0BF}'] = powerlineDefinitions['\u{E0B9}'];
|
||||
|
||||
/**
|
||||
* Try drawing a custom block element or box drawing character, returning whether it was
|
||||
|
||||
@@ -304,12 +304,6 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
break;
|
||||
}
|
||||
|
||||
if (dim) {
|
||||
// Blend here instead of using opacity because transparent colors mess with clipping the
|
||||
// glyph's bounding box
|
||||
result = color.blend(this._config.colors.background, color.multiplyOpacity(result, DIM_OPACITY));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -429,12 +423,12 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
// Allow 1 cell width per character, with a minimum of 2 (CJK), plus some padding. This is used
|
||||
// to draw the glyph to the canvas as well as to restrict the bounding box search to ensure
|
||||
// giant ligatures (eg. =====>) don't impact overall performance.
|
||||
const allowedWidth = this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2;
|
||||
const allowedWidth = Math.min(this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2, this._textureSize);
|
||||
if (this._tmpCanvas.width < allowedWidth) {
|
||||
this._tmpCanvas.width = allowedWidth;
|
||||
}
|
||||
// Include line height when drawing glyphs
|
||||
const allowedHeight = this._config.deviceCellHeight + TMP_CANVAS_GLYPH_PADDING * 4;
|
||||
const allowedHeight = Math.min(this._config.deviceCellHeight + TMP_CANVAS_GLYPH_PADDING * 4, this._textureSize);
|
||||
if (this._tmpCanvas.height < allowedHeight) {
|
||||
this._tmpCanvas.height = allowedHeight;
|
||||
}
|
||||
@@ -455,6 +449,7 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
const italic = !!this._workAttributeData.isItalic();
|
||||
const underline = !!this._workAttributeData.isUnderline();
|
||||
const strikethrough = !!this._workAttributeData.isStrikethrough();
|
||||
const overline = !!this._workAttributeData.isOverline();
|
||||
let fgColor = this._workAttributeData.getFgColor();
|
||||
let fgColorMode = this._workAttributeData.getFgColorMode();
|
||||
let bgColor = this._workAttributeData.getBgColor();
|
||||
@@ -638,12 +633,24 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
}
|
||||
}
|
||||
|
||||
// Overline
|
||||
if (overline) {
|
||||
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * this._config.devicePixelRatio / 15));
|
||||
const yOffset = lineWidth % 2 === 1 ? 0.5 : 0;
|
||||
this._tmpCtx.lineWidth = lineWidth;
|
||||
this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
|
||||
this._tmpCtx.beginPath();
|
||||
this._tmpCtx.moveTo(padding, padding + yOffset);
|
||||
this._tmpCtx.lineTo(padding + this._config.deviceCharWidth * chWidth, padding + yOffset);
|
||||
this._tmpCtx.stroke();
|
||||
}
|
||||
|
||||
// Draw the character
|
||||
if (!customGlyph) {
|
||||
this._tmpCtx.fillText(chars, padding, padding + this._config.deviceCharHeight);
|
||||
}
|
||||
|
||||
// If this charcater is underscore and beyond the cell bounds, shift it up until it is visible
|
||||
// If this character is underscore and beyond the cell bounds, shift it up until it is visible
|
||||
// even on the bottom row, try for a maximum of 5 pixels.
|
||||
if (chars === '_' && !this._config.allowTransparency) {
|
||||
let isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight), backgroundColor, foregroundColor, enableClearThresholdCheck);
|
||||
|
||||
+23
-19
@@ -57,7 +57,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
||||
|
||||
protected _inputHandler: InputHandler;
|
||||
private _writeBuffer: WriteBuffer;
|
||||
private _windowsMode: IDisposable | undefined;
|
||||
private _windowsWrappingHeuristics: IDisposable | undefined;
|
||||
|
||||
private readonly _onBinary = this.register(new EventEmitter<string>());
|
||||
public readonly onBinary = this._onBinary.event;
|
||||
@@ -131,7 +131,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
||||
this.register(forwardEvent(this.coreService.onBinary, this._onBinary));
|
||||
this.register(this.coreService.onRequestScrollToBottom(() => this.scrollToBottom()));
|
||||
this.register(this.coreService.onUserInput(() => this._writeBuffer.handleUserInput()));
|
||||
this.register(this.optionsService.onSpecificOptionChange('windowsMode', e => this._handleWindowsModeOptionChange(e)));
|
||||
this.register(this.optionsService.onMultipleOptionChange(['windowsMode', 'windowsPty'], () => this._handleWindowsPtyOptionChange()));
|
||||
this.register(this._bufferService.onScroll(event => {
|
||||
this._onScroll.fire({ position: this._bufferService.buffer.ydisp, source: ScrollSource.TERMINAL });
|
||||
this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop, this._bufferService.buffer.scrollBottom);
|
||||
@@ -146,8 +146,8 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
||||
this.register(forwardEvent(this._writeBuffer.onWriteParsed, this._onWriteParsed));
|
||||
|
||||
this.register(toDisposable(() => {
|
||||
this._windowsMode?.dispose();
|
||||
this._windowsMode = undefined;
|
||||
this._windowsWrappingHeuristics?.dispose();
|
||||
this._windowsWrappingHeuristics = undefined;
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -250,9 +250,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
||||
}
|
||||
|
||||
protected _setup(): void {
|
||||
if (this.optionsService.rawOptions.windowsMode) {
|
||||
this._enableWindowsMode();
|
||||
}
|
||||
this._handleWindowsPtyOptionChange();
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
@@ -263,30 +261,36 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
||||
this.coreMouseService.reset();
|
||||
}
|
||||
|
||||
private _handleWindowsModeOptionChange(value: boolean): void {
|
||||
|
||||
private _handleWindowsPtyOptionChange(): void {
|
||||
let value = false;
|
||||
const windowsPty = this.optionsService.rawOptions.windowsPty;
|
||||
if (windowsPty && windowsPty.buildNumber !== undefined && windowsPty.buildNumber !== undefined) {
|
||||
value = !!(windowsPty.backend === 'conpty' && windowsPty.buildNumber < 21376);
|
||||
} else if (this.optionsService.rawOptions.windowsMode) {
|
||||
value = true;
|
||||
}
|
||||
if (value) {
|
||||
this._enableWindowsMode();
|
||||
this._enableWindowsWrappingHeuristics();
|
||||
} else {
|
||||
this._windowsMode?.dispose();
|
||||
this._windowsMode = undefined;
|
||||
this._windowsWrappingHeuristics?.dispose();
|
||||
this._windowsWrappingHeuristics = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected _enableWindowsMode(): void {
|
||||
if (!this._windowsMode) {
|
||||
protected _enableWindowsWrappingHeuristics(): void {
|
||||
if (!this._windowsWrappingHeuristics) {
|
||||
const disposables: IDisposable[] = [];
|
||||
disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService)));
|
||||
disposables.push(this.registerCsiHandler({ final: 'H' }, () => {
|
||||
updateWindowsModeWrappedState(this._bufferService);
|
||||
return false;
|
||||
}));
|
||||
this._windowsMode = {
|
||||
dispose: () => {
|
||||
for (const d of disposables) {
|
||||
d.dispose();
|
||||
}
|
||||
this._windowsWrappingHeuristics = toDisposable(() => {
|
||||
for (const d of disposables) {
|
||||
d.dispose();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2222,7 +2222,7 @@ describe('InputHandler', () => {
|
||||
});
|
||||
it('ANSI 2 (keyboard action mode)', async () => {
|
||||
await inputHandler.parseP('\x1b[2$p');
|
||||
assert.deepEqual(reportStack.pop(), '\x1b[2;3$y'); // always set
|
||||
assert.deepEqual(reportStack.pop(), '\x1b[2;4$y'); // always reset
|
||||
});
|
||||
it('ANSI 4 (insert mode)', async () => {
|
||||
await inputHandler.parseP('\x1b[4$p');
|
||||
@@ -2236,7 +2236,7 @@ describe('InputHandler', () => {
|
||||
});
|
||||
it('ANSI 12 (send/receive)', async () => {
|
||||
await inputHandler.parseP('\x1b[12$p');
|
||||
assert.deepEqual(reportStack.pop(), '\x1b[12;4$y'); // always reset
|
||||
assert.deepEqual(reportStack.pop(), '\x1b[12;3$y'); // always set
|
||||
});
|
||||
it('ANSI 20 (newline mode)', async () => {
|
||||
await inputHandler.parseP('\x1b[20$p');
|
||||
@@ -2280,7 +2280,7 @@ describe('InputHandler', () => {
|
||||
});
|
||||
it('DEC privates perma modes', async () => {
|
||||
// [mode number, state value]
|
||||
const perma = [[3, 0], [8, 3], [1005, 4], [1015, 4], [1048, 1]];
|
||||
const perma = [[3, 0], [8, 3], [67, 4], [1005, 4], [1015, 4], [1048, 1]];
|
||||
for (const [mode, value] of perma) {
|
||||
await inputHandler.parseP(`\x1b[?${mode}$p`);
|
||||
assert.deepEqual(reportStack.pop(), `\x1b[?${mode};${value}$y`);
|
||||
|
||||
@@ -711,6 +711,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._bufferService.scroll(this._eraseAttrData());
|
||||
} else if (this._activeBuffer.y >= this._bufferService.rows) {
|
||||
this._activeBuffer.y = this._bufferService.rows - 1;
|
||||
} else {
|
||||
// There was an explicit line feed (not just a carriage return), so clear the wrapped state of
|
||||
// the line. This is particularly important on conpty/Windows where revisiting lines to
|
||||
// reprint is common, especially on resize. Note that the windowsMode wrapped line heuristics
|
||||
// can mess with this so windowsMode should be disabled, which is recommended on Windows build
|
||||
// 21376 and above.
|
||||
this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false;
|
||||
}
|
||||
// If the end of the line is hit, prevent this action from wrapping around to the next line.
|
||||
if (this._activeBuffer.x >= this._bufferService.cols) {
|
||||
@@ -1145,10 +1152,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @param y row index
|
||||
*/
|
||||
private _resetBufferLine(y: number, respectProtect: boolean = false): void {
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
||||
line.fill(this._activeBuffer.getNullCell(this._eraseAttrData()), respectProtect);
|
||||
this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase + y);
|
||||
line.isWrapped = false;
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y);
|
||||
if (line) {
|
||||
line.fill(this._activeBuffer.getNullCell(this._eraseAttrData()), respectProtect);
|
||||
this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase + y);
|
||||
line.isWrapped = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2216,9 +2225,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const p = params.params[0];
|
||||
|
||||
if (ansi) {
|
||||
if (p === 2) return f(p, V.PERMANENTLY_SET);
|
||||
if (p === 2) return f(p, V.PERMANENTLY_RESET);
|
||||
if (p === 4) return f(p, b2v(cs.modes.insertMode));
|
||||
if (p === 12) return f(p, V.PERMANENTLY_RESET);
|
||||
if (p === 12) return f(p, V.PERMANENTLY_SET);
|
||||
if (p === 20) return f(p, b2v(opts.convertEol));
|
||||
return f(p, V.NOT_RECOGNIZED);
|
||||
}
|
||||
@@ -2233,6 +2242,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
if (p === 25) return f(p, b2v(!cs.isCursorHidden));
|
||||
if (p === 45) return f(p, b2v(dm.reverseWraparound));
|
||||
if (p === 66) return f(p, b2v(dm.applicationKeypad));
|
||||
if (p === 67) return f(p, V.PERMANENTLY_RESET);
|
||||
if (p === 1000) return f(p, b2v(mouseProtocol === 'VT200'));
|
||||
if (p === 1002) return f(p, b2v(mouseProtocol === 'DRAG'));
|
||||
if (p === 1003) return f(p, b2v(mouseProtocol === 'ANY'));
|
||||
@@ -2418,6 +2428,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* | 47 | Background color: White. | #Y |
|
||||
* | 48 | Background color: Extended color. | #P[Support for RGB and indexed colors, see below.] |
|
||||
* | 49 | Background color: Default (original). | #Y |
|
||||
* | 53 | Overlined. | #Y |
|
||||
* | 55 | Not Overlined. | #Y |
|
||||
* | 58 | Underline color: Extended color. | #P[Support for RGB and indexed colors, see below.] |
|
||||
* | 90 - 97 | Bright foreground color (analogous to 30 - 37). | #Y |
|
||||
* | 100 - 107 | Bright background color (analogous to 40 - 47). | #Y |
|
||||
@@ -2544,6 +2556,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
} else if (p === 38 || p === 48 || p === 58) {
|
||||
// fg color 256 and RGB
|
||||
i += this._extractColor(params, i, attr);
|
||||
} else if (p === 53) {
|
||||
// overline
|
||||
attr.bg |= BgFlags.OVERLINE;
|
||||
} else if (p === 55) {
|
||||
// not overline
|
||||
attr.bg &= ~BgFlags.OVERLINE;
|
||||
} else if (p === 59) {
|
||||
attr.extended = attr.extended.clone();
|
||||
attr.extended.underlineColor = -1;
|
||||
|
||||
Vendored
+1
@@ -165,6 +165,7 @@ export interface IAttributeData {
|
||||
isDim(): number;
|
||||
isStrikethrough(): number;
|
||||
isProtected(): number;
|
||||
isOverline(): number;
|
||||
|
||||
/**
|
||||
* The color mode of the foreground color which determines how to decode {@link getFgColor},
|
||||
|
||||
@@ -47,6 +47,7 @@ export class AttributeData implements IAttributeData {
|
||||
public isDim(): number { return this.bg & BgFlags.DIM; }
|
||||
public isStrikethrough(): number { return this.fg & FgFlags.STRIKETHROUGH; }
|
||||
public isProtected(): number { return this.bg & BgFlags.PROTECTED; }
|
||||
public isOverline(): number { return this.bg & BgFlags.OVERLINE; }
|
||||
|
||||
// color modes
|
||||
public getFgColorMode(): number { return this.fg & Attributes.CM_MASK; }
|
||||
|
||||
@@ -177,7 +177,7 @@ export class Buffer implements IBuffer {
|
||||
if (this._rows < newRows) {
|
||||
for (let y = this._rows; y < newRows; y++) {
|
||||
if (this.lines.length < newRows + this.ybase) {
|
||||
if (this._optionsService.rawOptions.windowsMode) {
|
||||
if (this._optionsService.rawOptions.windowsMode || this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined) {
|
||||
// Just add the new missing rows on Windows as conpty reprints the screen with it's
|
||||
// view of the world. Once a line enters scrollback for conpty it remains there
|
||||
this.lines.push(new BufferLine(newCols, nullCell));
|
||||
@@ -290,6 +290,10 @@ export class Buffer implements IBuffer {
|
||||
}
|
||||
|
||||
private get _isReflowEnabled(): boolean {
|
||||
const windowsPty = this._optionsService.rawOptions.windowsPty;
|
||||
if (windowsPty && windowsPty.buildNumber) {
|
||||
return this._hasScrollback && windowsPty.backend === 'conpty' && windowsPty.buildNumber >= 21376;
|
||||
}
|
||||
return this._hasScrollback && !this._optionsService.rawOptions.windowsMode;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,8 @@ export const enum BgFlags {
|
||||
ITALIC = 0x4000000,
|
||||
DIM = 0x8000000,
|
||||
HAS_EXTENDED = 0x10000000,
|
||||
PROTECTED = 0x20000000
|
||||
PROTECTED = 0x20000000,
|
||||
OVERLINE = 0x40000000
|
||||
}
|
||||
|
||||
export const enum ExtFlags {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { DecorationService } from './DecorationService';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { IMarker } from 'common/Types';
|
||||
import { Disposable } from 'common/Lifecycle';
|
||||
|
||||
const fakeMarker: IMarker = Object.freeze(new class extends Disposable {
|
||||
public readonly id = 1;
|
||||
public readonly line = 1;
|
||||
public readonly isDisposed = false;
|
||||
public readonly onDispose = new EventEmitter<void>().event;
|
||||
}());
|
||||
|
||||
describe('DecorationService', () => {
|
||||
it('should set isDisposed to true after dispose', () => {
|
||||
const service = new DecorationService();
|
||||
const decoration = service.registerDecoration({
|
||||
marker: fakeMarker
|
||||
});
|
||||
assert.ok(decoration);
|
||||
assert.isFalse(decoration!.isDisposed);
|
||||
decoration!.dispose();
|
||||
assert.isTrue(decoration!.isDisposed);
|
||||
});
|
||||
});
|
||||
@@ -104,7 +104,7 @@ export class DecorationService extends Disposable implements IDecorationService
|
||||
class Decoration extends Disposable implements IInternalDecoration {
|
||||
public readonly marker: IMarker;
|
||||
public element: HTMLElement | undefined;
|
||||
public isDisposed: boolean = false;
|
||||
public get isDisposed(): boolean { return this._isDisposed; }
|
||||
|
||||
public readonly onRenderEmitter = this.register(new EventEmitter<HTMLElement>());
|
||||
public readonly onRender = this.onRenderEmitter.event;
|
||||
|
||||
@@ -43,6 +43,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
|
||||
rightClickSelectsWord: isMac,
|
||||
windowOptions: {},
|
||||
windowsMode: false,
|
||||
windowsPty: {},
|
||||
wordSeparator: ' ()[]{}\',"`',
|
||||
altClickMovesCursor: true,
|
||||
convertEol: false,
|
||||
@@ -177,12 +178,16 @@ export class OptionsService extends Disposable implements IOptionsService {
|
||||
if (value <= 0) {
|
||||
throw new Error(`${key} cannot be less than or equal to 0, value: ${value}`);
|
||||
}
|
||||
break;
|
||||
case 'rows':
|
||||
case 'cols':
|
||||
if (!value && value !== 0) {
|
||||
throw new Error(`${key} must be numeric, value: ${value}`);
|
||||
}
|
||||
break;
|
||||
case 'windowsPty':
|
||||
value = value ?? {};
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IEvent, IEventEmitter } from 'common/EventEmitter';
|
||||
import { IBuffer, IBufferSet } from 'common/buffer/Types';
|
||||
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes, IAttributeData, ScrollSource, IDisposable, IColor, CursorStyle, IOscLinkData } from 'common/Types';
|
||||
import { createDecorator } from 'common/services/ServiceRegistry';
|
||||
import { IDecorationOptions, IDecoration, ILinkHandler } from 'xterm';
|
||||
import { IDecorationOptions, IDecoration, ILinkHandler, IWindowsPty } from 'xterm';
|
||||
|
||||
export const IBufferService = createDecorator<IBufferService>('BufferService');
|
||||
export interface IBufferService {
|
||||
@@ -242,6 +242,7 @@ export interface ITerminalOptions {
|
||||
tabStopWidth?: number;
|
||||
theme?: ITheme;
|
||||
windowsMode?: boolean;
|
||||
windowsPty?: IWindowsPty;
|
||||
windowOptions?: IWindowOptions;
|
||||
wordSeparator?: string;
|
||||
overviewRulerWidth?: number;
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('Headless API Tests', function (): void {
|
||||
|
||||
it('Proposed API check', async () => {
|
||||
term = new Terminal({ allowProposedApi: false });
|
||||
throws(() => term.markers, (error) => error.message === 'You must set the allowProposedApi option to true to use proposed API');
|
||||
throws(() => term.markers, (error: any) => error.message === 'You must set the allowProposedApi option to true to use proposed API');
|
||||
});
|
||||
|
||||
it('write', async () => {
|
||||
|
||||
Vendored
+41
@@ -183,9 +183,34 @@ declare module 'xterm-headless' {
|
||||
* - Reflow is disabled.
|
||||
* - Lines are assumed to be wrapped if the last character of the line is
|
||||
* not whitespace.
|
||||
*
|
||||
* When using conpty on Windows 11 version >= 21376, it is recommended to
|
||||
* disable this because native text wrapping sequences are output correctly
|
||||
* thanks to https://github.com/microsoft/terminal/issues/405
|
||||
*
|
||||
* @deprecated Use {@link windowsPty}. This value will be ignored if
|
||||
* windowsPty is set.
|
||||
*/
|
||||
windowsMode?: boolean;
|
||||
|
||||
/**
|
||||
* Compatibility information when the pty is known to be hosted on Windows.
|
||||
* Setting this will turn on certain heuristics/workarounds depending on the
|
||||
* values:
|
||||
*
|
||||
* - `if (!!windowsCompat)`
|
||||
* - When increasing the rows in the terminal, the amount increased into
|
||||
* the scrollback. This is done because ConPTY does not behave like
|
||||
* expect scrollback to come back into the viewport, instead it makes
|
||||
* empty rows at of the viewport. Not having this behavior can result in
|
||||
* missing data as the rows get replaced.
|
||||
* - `if !(backend === 'conpty' && buildNumber >= 21376)`
|
||||
* - Reflow is disabled
|
||||
* - Lines are assumed to be wrapped if the last character of the line is
|
||||
* not whitespace.
|
||||
*/
|
||||
windowsPty?: IWindowsPty;
|
||||
|
||||
/**
|
||||
* A string containing all characters that are considered word separated by the
|
||||
* double click to select work logic.
|
||||
@@ -265,6 +290,20 @@ declare module 'xterm-headless' {
|
||||
extendedAnsi?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pty information for Windows.
|
||||
*/
|
||||
export interface IWindowsPty {
|
||||
/**
|
||||
* What pty emulation backend is being used.
|
||||
*/
|
||||
backend?: 'conpty' | 'winpty';
|
||||
/**
|
||||
* The Windows build version (eg. 19045)
|
||||
*/
|
||||
buildNumber?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that can be disposed via a dispose function.
|
||||
*/
|
||||
@@ -999,6 +1038,8 @@ declare module 'xterm-headless' {
|
||||
isInvisible(): number;
|
||||
/** Whether the cell has the strikethrough attribute (CSI 9 m). */
|
||||
isStrikethrough(): number;
|
||||
/** Whether the cell has the overline attribute (CSI 53 m). */
|
||||
isOverline(): number;
|
||||
|
||||
/** Whether the cell is using the RGB foreground color mode. */
|
||||
isFgRGB(): boolean;
|
||||
|
||||
Vendored
+46
-8
@@ -238,9 +238,30 @@ declare module 'xterm' {
|
||||
* When using conpty on Windows 11 version >= 21376, it is recommended to
|
||||
* disable this because native text wrapping sequences are output correctly
|
||||
* thanks to https://github.com/microsoft/terminal/issues/405
|
||||
*
|
||||
* @deprecated Use {@link windowsPty}. This value will be ignored if
|
||||
* windowsPty is set.
|
||||
*/
|
||||
windowsMode?: boolean;
|
||||
|
||||
/**
|
||||
* Compatibility information when the pty is known to be hosted on Windows.
|
||||
* Setting this will turn on certain heuristics/workarounds depending on the
|
||||
* values:
|
||||
*
|
||||
* - `if (backend !== undefined || buildNumber !== undefined)`
|
||||
* - When increasing the rows in the terminal, the amount increased into
|
||||
* the scrollback. This is done because ConPTY does not behave like
|
||||
* expect scrollback to come back into the viewport, instead it makes
|
||||
* empty rows at of the viewport. Not having this behavior can result in
|
||||
* missing data as the rows get replaced.
|
||||
* - `if !(backend === 'conpty' && buildNumber >= 21376)`
|
||||
* - Reflow is disabled
|
||||
* - Lines are assumed to be wrapped if the last character of the line is
|
||||
* not whitespace.
|
||||
*/
|
||||
windowsPty?: IWindowsPty;
|
||||
|
||||
/**
|
||||
* A string containing all characters that are considered word separated by the
|
||||
* double click to select work logic.
|
||||
@@ -330,6 +351,20 @@ declare module 'xterm' {
|
||||
extendedAnsi?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pty information for Windows.
|
||||
*/
|
||||
export interface IWindowsPty {
|
||||
/**
|
||||
* What pty emulation backend is being used.
|
||||
*/
|
||||
backend?: 'conpty' | 'winpty';
|
||||
/**
|
||||
* The Windows build version (eg. 19045)
|
||||
*/
|
||||
buildNumber?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that can be disposed via a dispose function.
|
||||
*/
|
||||
@@ -498,11 +533,6 @@ declare module 'xterm' {
|
||||
* being printed to the terminal when `screenReaderMode` is enabled.
|
||||
*/
|
||||
tooMuchOutput: string;
|
||||
|
||||
/**
|
||||
* The aria label for the accessibility buffer
|
||||
*/
|
||||
accessibilityBuffer: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -959,12 +989,11 @@ declare module 'xterm' {
|
||||
deregisterCharacterJoiner(joinerId: number): void;
|
||||
|
||||
/**
|
||||
* Adds a marker to the normal buffer and returns it. If the alt buffer is
|
||||
* active, undefined is returned.
|
||||
* Adds a marker to the normal buffer and returns it.
|
||||
* @param cursorYOffset The y position offset of the marker from the cursor.
|
||||
* @returns The new marker or undefined.
|
||||
*/
|
||||
registerMarker(cursorYOffset?: number): IMarker | undefined;
|
||||
registerMarker(cursorYOffset?: number): IMarker;
|
||||
|
||||
/**
|
||||
* (EXPERIMENTAL) Adds a decoration to the terminal using
|
||||
@@ -1360,6 +1389,13 @@ declare module 'xterm' {
|
||||
getNullCell(): IBufferCell;
|
||||
}
|
||||
|
||||
export interface IBufferElementProvider {
|
||||
/**
|
||||
* Provides a document fragment or HTMLElement containing the buffer elements.
|
||||
*/
|
||||
provideBufferElements(): DocumentFragment | HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the terminal's set of buffers.
|
||||
*/
|
||||
@@ -1515,6 +1551,8 @@ declare module 'xterm' {
|
||||
isInvisible(): number;
|
||||
/** Whether the cell has the strikethrough attribute (CSI 9 m). */
|
||||
isStrikethrough(): number;
|
||||
/** Whether the cell has the overline attribute (CSI 53 m). */
|
||||
isOverline(): number;
|
||||
|
||||
/** Whether the cell is using the RGB foreground color mode. */
|
||||
isFgRGB(): boolean;
|
||||
|
||||
@@ -340,10 +340,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/deep-equal/-/deep-equal-1.0.1.tgz#71cfabb247c22bcc16d536111f50c0ed12476b03"
|
||||
integrity sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg==
|
||||
|
||||
"@types/eslint-scope@^3.7.0":
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86"
|
||||
integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==
|
||||
"@types/eslint-scope@^3.7.3":
|
||||
version "3.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
|
||||
integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==
|
||||
dependencies:
|
||||
"@types/eslint" "*"
|
||||
"@types/estree" "*"
|
||||
@@ -356,15 +356,10 @@
|
||||
"@types/estree" "*"
|
||||
"@types/json-schema" "*"
|
||||
|
||||
"@types/estree@*", "@types/estree@^0.0.47":
|
||||
version "0.0.47"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4"
|
||||
integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==
|
||||
|
||||
"@types/estree@^0.0.50":
|
||||
version "0.0.50"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
|
||||
integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
|
||||
"@types/estree@*", "@types/estree@^0.0.51":
|
||||
version "0.0.51"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
|
||||
integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
|
||||
|
||||
"@types/glob@^7.2.0":
|
||||
version "7.2.0"
|
||||
@@ -388,11 +383,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
|
||||
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
|
||||
|
||||
"@types/json-schema@^7.0.6":
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
|
||||
integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
|
||||
|
||||
"@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||
version "7.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
|
||||
@@ -435,10 +425,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.50.tgz#14ba5198f1754ffd0472a2f84ab433b45ee0b65e"
|
||||
integrity sha512-+9axpWx2b2JCVovr7Ilgt96uc6C1zBKOQMpGtRbWT9IoR/8ue32GGMfGA4woP8QyP2gBs6GQWEVM3tCybGCxDA==
|
||||
|
||||
"@types/node@^14.14.44":
|
||||
version "14.14.44"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.44.tgz#df7503e6002847b834371c004b372529f3f85215"
|
||||
integrity sha512-+gaugz6Oce6ZInfI/tK4Pq5wIIkJMEJUu92RB3Eu93mtj4wjjjz9EB5mLp5s1pSsLXdC/CPut/xF20ZzAQJbTA==
|
||||
"@types/node@^18.16.0":
|
||||
version "18.16.16"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.16.tgz#3b64862856c7874ccf7439e6bab872d245c86d8e"
|
||||
integrity sha512-NpaM49IGQQAUlBhHMF82QH80J08os4ZmyF9MkpCzWAGuOHqE4gTEbhzd7L3l5LmWuZ6E0OiC1FweQ4tsiW35+g==
|
||||
|
||||
"@types/parse5@*":
|
||||
version "6.0.2"
|
||||
@@ -548,14 +538,6 @@
|
||||
"@typescript-eslint/types" "5.3.0"
|
||||
eslint-visitor-keys "^3.0.0"
|
||||
|
||||
"@webassemblyjs/ast@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f"
|
||||
integrity sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==
|
||||
dependencies:
|
||||
"@webassemblyjs/helper-numbers" "1.11.0"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.0"
|
||||
|
||||
"@webassemblyjs/ast@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
|
||||
@@ -564,45 +546,21 @@
|
||||
"@webassemblyjs/helper-numbers" "1.11.1"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.1"
|
||||
|
||||
"@webassemblyjs/floating-point-hex-parser@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz#34d62052f453cd43101d72eab4966a022587947c"
|
||||
integrity sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==
|
||||
|
||||
"@webassemblyjs/floating-point-hex-parser@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
|
||||
integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==
|
||||
|
||||
"@webassemblyjs/helper-api-error@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz#aaea8fb3b923f4aaa9b512ff541b013ffb68d2d4"
|
||||
integrity sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==
|
||||
|
||||
"@webassemblyjs/helper-api-error@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
|
||||
integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==
|
||||
|
||||
"@webassemblyjs/helper-buffer@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz#d026c25d175e388a7dbda9694e91e743cbe9b642"
|
||||
integrity sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==
|
||||
|
||||
"@webassemblyjs/helper-buffer@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
|
||||
integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==
|
||||
|
||||
"@webassemblyjs/helper-numbers@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz#7ab04172d54e312cc6ea4286d7d9fa27c88cd4f9"
|
||||
integrity sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==
|
||||
dependencies:
|
||||
"@webassemblyjs/floating-point-hex-parser" "1.11.0"
|
||||
"@webassemblyjs/helper-api-error" "1.11.0"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webassemblyjs/helper-numbers@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
|
||||
@@ -612,26 +570,11 @@
|
||||
"@webassemblyjs/helper-api-error" "1.11.1"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webassemblyjs/helper-wasm-bytecode@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz#85fdcda4129902fe86f81abf7e7236953ec5a4e1"
|
||||
integrity sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==
|
||||
|
||||
"@webassemblyjs/helper-wasm-bytecode@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
|
||||
integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==
|
||||
|
||||
"@webassemblyjs/helper-wasm-section@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz#9ce2cc89300262509c801b4af113d1ca25c1a75b"
|
||||
integrity sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.0"
|
||||
"@webassemblyjs/helper-buffer" "1.11.0"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.0"
|
||||
"@webassemblyjs/wasm-gen" "1.11.0"
|
||||
|
||||
"@webassemblyjs/helper-wasm-section@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
|
||||
@@ -642,13 +585,6 @@
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.1"
|
||||
"@webassemblyjs/wasm-gen" "1.11.1"
|
||||
|
||||
"@webassemblyjs/ieee754@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz#46975d583f9828f5d094ac210e219441c4e6f5cf"
|
||||
integrity sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==
|
||||
dependencies:
|
||||
"@xtuc/ieee754" "^1.2.0"
|
||||
|
||||
"@webassemblyjs/ieee754@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
|
||||
@@ -656,13 +592,6 @@
|
||||
dependencies:
|
||||
"@xtuc/ieee754" "^1.2.0"
|
||||
|
||||
"@webassemblyjs/leb128@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.0.tgz#f7353de1df38aa201cba9fb88b43f41f75ff403b"
|
||||
integrity sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==
|
||||
dependencies:
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webassemblyjs/leb128@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
|
||||
@@ -670,30 +599,11 @@
|
||||
dependencies:
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webassemblyjs/utf8@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.0.tgz#86e48f959cf49e0e5091f069a709b862f5a2cadf"
|
||||
integrity sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==
|
||||
|
||||
"@webassemblyjs/utf8@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
|
||||
integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==
|
||||
|
||||
"@webassemblyjs/wasm-edit@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz#ee4a5c9f677046a210542ae63897094c2027cb78"
|
||||
integrity sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.0"
|
||||
"@webassemblyjs/helper-buffer" "1.11.0"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.0"
|
||||
"@webassemblyjs/helper-wasm-section" "1.11.0"
|
||||
"@webassemblyjs/wasm-gen" "1.11.0"
|
||||
"@webassemblyjs/wasm-opt" "1.11.0"
|
||||
"@webassemblyjs/wasm-parser" "1.11.0"
|
||||
"@webassemblyjs/wast-printer" "1.11.0"
|
||||
|
||||
"@webassemblyjs/wasm-edit@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
|
||||
@@ -708,17 +618,6 @@
|
||||
"@webassemblyjs/wasm-parser" "1.11.1"
|
||||
"@webassemblyjs/wast-printer" "1.11.1"
|
||||
|
||||
"@webassemblyjs/wasm-gen@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz#3cdb35e70082d42a35166988dda64f24ceb97abe"
|
||||
integrity sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.0"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.0"
|
||||
"@webassemblyjs/ieee754" "1.11.0"
|
||||
"@webassemblyjs/leb128" "1.11.0"
|
||||
"@webassemblyjs/utf8" "1.11.0"
|
||||
|
||||
"@webassemblyjs/wasm-gen@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
|
||||
@@ -730,16 +629,6 @@
|
||||
"@webassemblyjs/leb128" "1.11.1"
|
||||
"@webassemblyjs/utf8" "1.11.1"
|
||||
|
||||
"@webassemblyjs/wasm-opt@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz#1638ae188137f4bb031f568a413cd24d32f92978"
|
||||
integrity sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.0"
|
||||
"@webassemblyjs/helper-buffer" "1.11.0"
|
||||
"@webassemblyjs/wasm-gen" "1.11.0"
|
||||
"@webassemblyjs/wasm-parser" "1.11.0"
|
||||
|
||||
"@webassemblyjs/wasm-opt@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
|
||||
@@ -750,18 +639,6 @@
|
||||
"@webassemblyjs/wasm-gen" "1.11.1"
|
||||
"@webassemblyjs/wasm-parser" "1.11.1"
|
||||
|
||||
"@webassemblyjs/wasm-parser@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz#3e680b8830d5b13d1ec86cc42f38f3d4a7700754"
|
||||
integrity sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.0"
|
||||
"@webassemblyjs/helper-api-error" "1.11.0"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.0"
|
||||
"@webassemblyjs/ieee754" "1.11.0"
|
||||
"@webassemblyjs/leb128" "1.11.0"
|
||||
"@webassemblyjs/utf8" "1.11.0"
|
||||
|
||||
"@webassemblyjs/wasm-parser@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
|
||||
@@ -774,14 +651,6 @@
|
||||
"@webassemblyjs/leb128" "1.11.1"
|
||||
"@webassemblyjs/utf8" "1.11.1"
|
||||
|
||||
"@webassemblyjs/wast-printer@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz#680d1f6a5365d6d401974a8e949e05474e1fab7e"
|
||||
integrity sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.0"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webassemblyjs/wast-printer@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
|
||||
@@ -858,10 +727,10 @@ acorn@^7.1.1:
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||
|
||||
acorn@^8.2.1, acorn@^8.4.1, acorn@^8.5.0:
|
||||
version "8.7.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
|
||||
integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
|
||||
acorn@^8.5.0, acorn@^8.7.1:
|
||||
version "8.8.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
|
||||
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
|
||||
|
||||
agent-base@6:
|
||||
version "6.0.2"
|
||||
@@ -1524,18 +1393,10 @@ encodeurl@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||
|
||||
enhanced-resolve@^5.0.0, enhanced-resolve@^5.8.3:
|
||||
version "5.8.3"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0"
|
||||
integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
|
||||
enhanced-resolve@^5.8.0:
|
||||
version "5.8.2"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b"
|
||||
integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==
|
||||
enhanced-resolve@^5.0.0, enhanced-resolve@^5.10.0:
|
||||
version "5.12.0"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634"
|
||||
integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
@@ -1588,11 +1449,6 @@ es-get-iterator@^1.1.1:
|
||||
is-string "^1.0.5"
|
||||
isarray "^2.0.5"
|
||||
|
||||
es-module-lexer@^0.4.0:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e"
|
||||
integrity sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==
|
||||
|
||||
es-module-lexer@^0.9.0:
|
||||
version "0.9.3"
|
||||
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
|
||||
@@ -2131,15 +1987,10 @@ globby@^11.0.4:
|
||||
merge2 "^1.3.0"
|
||||
slash "^3.0.0"
|
||||
|
||||
graceful-fs@^4.1.15, graceful-fs@^4.1.2:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
||||
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
|
||||
|
||||
graceful-fs@^4.2.4:
|
||||
version "4.2.6"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
|
||||
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
|
||||
graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
|
||||
version "4.2.10"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
|
||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||
|
||||
has-bigints@^1.0.1:
|
||||
version "1.0.1"
|
||||
@@ -2553,15 +2404,6 @@ javascript-natural-sort@^0.7.1:
|
||||
resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59"
|
||||
integrity sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=
|
||||
|
||||
jest-worker@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
|
||||
integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
merge-stream "^2.0.0"
|
||||
supports-color "^7.0.0"
|
||||
|
||||
jest-worker@^27.0.6:
|
||||
version "27.3.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.3.1.tgz#0def7feae5b8042be38479799aeb7b5facac24b2"
|
||||
@@ -2634,10 +2476,10 @@ jsesc@^2.5.1:
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
|
||||
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
|
||||
|
||||
json-parse-better-errors@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
|
||||
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
|
||||
json-parse-even-better-errors@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
|
||||
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
|
||||
|
||||
json-schema-traverse@^0.4.1:
|
||||
version "0.4.1"
|
||||
@@ -2779,48 +2621,12 @@ micromatch@^4.0.0, micromatch@^4.0.4:
|
||||
braces "^3.0.1"
|
||||
picomatch "^2.2.3"
|
||||
|
||||
mime-db@1.44.0:
|
||||
version "1.44.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
|
||||
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
|
||||
|
||||
mime-db@1.47.0:
|
||||
version "1.47.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c"
|
||||
integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==
|
||||
|
||||
mime-db@1.50.0:
|
||||
version "1.50.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f"
|
||||
integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==
|
||||
|
||||
mime-db@1.52.0:
|
||||
version "1.52.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||
|
||||
mime-types@^2.1.12:
|
||||
version "2.1.33"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.33.tgz#1fa12a904472fafd068e48d9e8401f74d3f70edb"
|
||||
integrity sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==
|
||||
dependencies:
|
||||
mime-db "1.50.0"
|
||||
|
||||
mime-types@^2.1.27:
|
||||
version "2.1.30"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d"
|
||||
integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==
|
||||
dependencies:
|
||||
mime-db "1.47.0"
|
||||
|
||||
mime-types@~2.1.24:
|
||||
version "2.1.27"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
|
||||
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
|
||||
dependencies:
|
||||
mime-db "1.44.0"
|
||||
|
||||
mime-types@~2.1.34:
|
||||
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
@@ -3397,15 +3203,6 @@ saxes@^5.0.1:
|
||||
dependencies:
|
||||
xmlchars "^2.2.0"
|
||||
|
||||
schema-utils@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
|
||||
integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.6"
|
||||
ajv "^6.12.5"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
schema-utils@^3.1.0, schema-utils@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
|
||||
@@ -3470,13 +3267,6 @@ serialize-javascript@6.0.0, serialize-javascript@^6.0.0:
|
||||
dependencies:
|
||||
randombytes "^2.1.0"
|
||||
|
||||
serialize-javascript@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
|
||||
integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
|
||||
dependencies:
|
||||
randombytes "^2.1.0"
|
||||
|
||||
serve-static@1.14.2:
|
||||
version "1.14.2"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa"
|
||||
@@ -3540,11 +3330,6 @@ slash@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
||||
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
||||
|
||||
source-list-map@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
|
||||
|
||||
source-map-js@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
|
||||
@@ -3685,7 +3470,7 @@ supports-color@^5.3.0:
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
supports-color@^7.0.0, supports-color@^7.1.0:
|
||||
supports-color@^7.1.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
|
||||
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
|
||||
@@ -3702,18 +3487,6 @@ tapable@^2.1.1, tapable@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
|
||||
integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==
|
||||
|
||||
terser-webpack-plugin@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz#7effadee06f7ecfa093dbbd3e9ab23f5f3ed8673"
|
||||
integrity sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q==
|
||||
dependencies:
|
||||
jest-worker "^26.6.2"
|
||||
p-limit "^3.1.0"
|
||||
schema-utils "^3.0.0"
|
||||
serialize-javascript "^5.0.1"
|
||||
source-map "^0.6.1"
|
||||
terser "^5.5.1"
|
||||
|
||||
terser-webpack-plugin@^5.1.3:
|
||||
version "5.2.4"
|
||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz#ad1be7639b1cbe3ea49fab995cbe7224b31747a1"
|
||||
@@ -3726,7 +3499,7 @@ terser-webpack-plugin@^5.1.3:
|
||||
source-map "^0.6.1"
|
||||
terser "^5.7.2"
|
||||
|
||||
terser@^5.5.1, terser@^5.7.2:
|
||||
terser@^5.7.2:
|
||||
version "5.14.2"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10"
|
||||
integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==
|
||||
@@ -3935,18 +3708,10 @@ w3c-xmlserializer@^3.0.0:
|
||||
dependencies:
|
||||
xml-name-validator "^4.0.0"
|
||||
|
||||
watchpack@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7"
|
||||
integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==
|
||||
dependencies:
|
||||
glob-to-regexp "^0.4.1"
|
||||
graceful-fs "^4.1.2"
|
||||
|
||||
watchpack@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce"
|
||||
integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==
|
||||
watchpack@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
|
||||
integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
|
||||
dependencies:
|
||||
glob-to-regexp "^0.4.1"
|
||||
graceful-fs "^4.1.2"
|
||||
@@ -3989,77 +3754,40 @@ webpack-merge@^5.7.3:
|
||||
clone-deep "^4.0.1"
|
||||
wildcard "^2.0.0"
|
||||
|
||||
webpack-sources@^2.1.1:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac"
|
||||
integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==
|
||||
dependencies:
|
||||
source-list-map "^2.0.1"
|
||||
source-map "^0.6.1"
|
||||
webpack-sources@^3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||
|
||||
webpack-sources@^3.2.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.1.tgz#251a7d9720d75ada1469ca07dbb62f3641a05b6d"
|
||||
integrity sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA==
|
||||
|
||||
webpack@^5:
|
||||
version "5.37.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.37.0.tgz#2ab00f613faf494504eb2beef278dab7493cc39d"
|
||||
integrity sha512-yvdhgcI6QkQkDe1hINBAJ1UNevqNGTVaCkD2SSJcB8rcrNNl922RI8i2DXUAuNfANoxwsiXXEA4ZPZI9q2oGLA==
|
||||
webpack@^5, webpack@^5.61.0:
|
||||
version "5.76.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.0.tgz#f9fb9fb8c4a7dbdcd0d56a98e56b8a942ee2692c"
|
||||
integrity sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.0"
|
||||
"@types/estree" "^0.0.47"
|
||||
"@webassemblyjs/ast" "1.11.0"
|
||||
"@webassemblyjs/wasm-edit" "1.11.0"
|
||||
"@webassemblyjs/wasm-parser" "1.11.0"
|
||||
acorn "^8.2.1"
|
||||
browserslist "^4.14.5"
|
||||
chrome-trace-event "^1.0.2"
|
||||
enhanced-resolve "^5.8.0"
|
||||
es-module-lexer "^0.4.0"
|
||||
eslint-scope "^5.1.1"
|
||||
events "^3.2.0"
|
||||
glob-to-regexp "^0.4.1"
|
||||
graceful-fs "^4.2.4"
|
||||
json-parse-better-errors "^1.0.2"
|
||||
loader-runner "^4.2.0"
|
||||
mime-types "^2.1.27"
|
||||
neo-async "^2.6.2"
|
||||
schema-utils "^3.0.0"
|
||||
tapable "^2.1.1"
|
||||
terser-webpack-plugin "^5.1.1"
|
||||
watchpack "^2.0.0"
|
||||
webpack-sources "^2.1.1"
|
||||
|
||||
webpack@^5.61.0:
|
||||
version "5.61.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.61.0.tgz#fa827f0ee9bdfd141dd73c3e891e955ebd52fe7f"
|
||||
integrity sha512-fPdTuaYZ/GMGFm4WrPi2KRCqS1vDp773kj9S0iI5Uc//5cszsFEDgHNaX4Rj1vobUiU1dFIV3mA9k1eHeluFpw==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.0"
|
||||
"@types/estree" "^0.0.50"
|
||||
"@types/eslint-scope" "^3.7.3"
|
||||
"@types/estree" "^0.0.51"
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
"@webassemblyjs/wasm-edit" "1.11.1"
|
||||
"@webassemblyjs/wasm-parser" "1.11.1"
|
||||
acorn "^8.4.1"
|
||||
acorn "^8.7.1"
|
||||
acorn-import-assertions "^1.7.6"
|
||||
browserslist "^4.14.5"
|
||||
chrome-trace-event "^1.0.2"
|
||||
enhanced-resolve "^5.8.3"
|
||||
enhanced-resolve "^5.10.0"
|
||||
es-module-lexer "^0.9.0"
|
||||
eslint-scope "5.1.1"
|
||||
events "^3.2.0"
|
||||
glob-to-regexp "^0.4.1"
|
||||
graceful-fs "^4.2.4"
|
||||
json-parse-better-errors "^1.0.2"
|
||||
graceful-fs "^4.2.9"
|
||||
json-parse-even-better-errors "^2.3.1"
|
||||
loader-runner "^4.2.0"
|
||||
mime-types "^2.1.27"
|
||||
neo-async "^2.6.2"
|
||||
schema-utils "^3.1.0"
|
||||
tapable "^2.1.1"
|
||||
terser-webpack-plugin "^5.1.3"
|
||||
watchpack "^2.2.0"
|
||||
webpack-sources "^3.2.0"
|
||||
watchpack "^2.4.0"
|
||||
webpack-sources "^3.2.3"
|
||||
|
||||
whatwg-encoding@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
||||
Reference in New Issue
Block a user